【问题标题】:Set attribute in __new__ subclass with dynamic base_class使用动态 base_class 在 __new__ 子类中设置属性
【发布时间】:2018-07-07 11:48:27
【问题描述】:

我有 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

我可以改用setattr 来使用C 类,效果很好。

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__?

【问题讨论】:

  • 这里给出了为什么你会遇到这个错误的解释:stackoverflow.com/questions/45481440/…你能解释一下你还想对这个类做什么(除了给它添加一个属性吗?)因为有可能完全不同的方法可以实现您想要的。
  • 您使用的是 Python 2 还是 Python 3?后者没有内置的unicode 类型。
  • 这对我来说是“使用组合,而不是继承”。但即使你觉得有必要使用继承,为什么要在__new__ 中动态创建一个新类型(顺便说一句,每个实例都是唯一的)?这对我来说意义不大。只需使用工厂模式,并显式定义两个派生类型。
  • 注意,x = C('test', meta=10); y = C('test', meta=10); print(isinstance(y, C), isinstance(x, C)); print(type(x) == type(y))...

标签: python class unicode subclass built-in


【解决方案1】:

不太确定这里的最终目标是什么,但是要拥有一个根据初始值具有不同基类的类,但仍然具有相关的__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

【讨论】:

    【解决方案2】:

    出现这个错误的原因是你创建新类型的方式:

    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__ 描述符。

    【讨论】:

      猜你喜欢
      • 2022-01-02
      • 1970-01-01
      • 2021-06-17
      • 2012-10-23
      • 2013-02-18
      • 1970-01-01
      • 2020-01-08
      • 1970-01-01
      • 2014-04-04
      相关资源
      最近更新 更多