使用动态base_class在__new__子类中设置属性

时间:2018-01-28 18:54:03

标签: python class unicode subclass built-in

我有类A,其子类str通过kwargs

添加元数据
class A(str):
    def __new__(cls, value, **kwargs):
        obj = str.__new__(cls, value)
        obj.__dict__.update(kwargs)
        return obj

喜欢使用:

x = A('test', meta=10)
x.meta
10

现在,我也想处理unicode类型,所以我采用了以下方法。要动态检查类型并将其用作base_class

class B(object):
    def __new__(cls, value, **kwargs):
        base_class = str if isinstance(value, str) else unicode
        new_type = type(cls.__name__, (base_class,), dict(cls.__dict__))
        obj = base_class.__new__(new_type, value)
        obj.__dict__.update(kwargs)
        return obj

然而,这似乎是不正确的做法。给我一个

  

TypeError:'B'对象的描述符' dict '不适用于'B'对象

B('test', meta=10)
-----------------------------------------------------------------------

TypeError                             Traceback (most recent call last)

<ipython-input-533-206366568616> in <module>()
----> 1 B('tes', a=10).a

<ipython-input-528-91dc8e50cb78> in __new__(cls, value, **kw)
     11         new_type = type(cls.__name__, (base_class,), dict(cls.__dict__))
     12         obj = base_class.__new__(new_type, value)
---> 13         obj.__dict__.update(kw)
     14         return obj
     15

TypeError: descriptor '__dict__' for 'B' objects doesn't apply to 'B' object

我可以使用C来使用setattr代替,这可以正常使用。

class C(object):
    def __new__(cls, value, **kw):
        base_class = str if isinstance(value, str) else unicode
        new_type = type(cls.__name__, (base_class,), dict(cls.__dict__))
        obj = base_class.__new__(new_type, value)
        for k, v in kw.items():
            setattr(obj, k, v)
        return obj

x = C('test', meta=10)
x.meta
10

您能否帮我理解 class B 上错过的内容以及如何使用__dict__

2 个答案:

答案 0 :(得分:1)

发生此错误的原因是您创建新类型的原因:

new_type = type(cls.__name__, (base_class,), dict(cls.__dict__))

如果你看一下裸班的__dict__

class A(object):
    pass

print(dict(A.__dict__))

{
    '__dict__': <attribute '__dict__' of 'A' objects>,
    '__module__': '__main__',
    '__weakref__': <attribute '__weakref__' of 'A' objects>,
    '__doc__': None
}

__dict____weakref__是每个类的描述符,只有在必要时才创建__dict__,以节省空间。它不适用于内置对象。它仅适用于创建它的特定类型,因为每个类在内存中的位置可能略有不同,无法存储__dict__

解决此问题的一种方法是删除它们:

class B(object):
    def __new__(cls, value, **kwargs):
        base_class = str if isinstance(value, str) else unicode
        __dict__ = dict(cls.__dict__)
        del __dict__['__dict__'], __dict__['__weakref__']
        new_type = type(cls.__name__, (base_class,), __dict__)
        obj = base_class.__new__(new_type, value)
        obj.__dict__.update(kwargs)
        return obj

b = B(u'b', meta=10)
print(b.meta)  # 10

Python会自动添加它自己的__dict__描述符。

答案 1 :(得分:0)

不太确定这里的最终目标是什么,但是根据初始值有一个具有不同基类的类,但仍然有__dict__相关,你可以尝试多重继承,如:

代码:

class B(object):

    def __new__(cls, value, **kwargs):
        base_class = str if isinstance(value, str) else float

        class X(base_class, B):
            pass

        new_type = type(cls.__name__, (X,), dict(cls.__dict__))
        obj = base_class.__new__(new_type, value)
        obj.__dict__.update(kwargs)
        return obj

测试代码:

for val in ('test', 1.0):
    x = B(val, meta=10)
    print(x, x.meta, type(x), x.__dict__,
          isinstance(x, str), isinstance(x, float))

结果:

test 10 <class '__main__.B'> {'meta': 10} True False
1.0 10 <class '__main__.B'> {'meta': 10} False True