Python在__new__中创建对象期间设置属性

时间:2019-01-25 03:44:03

标签: python object metaprogramming dynamic-class-creation

使用__new__自定义元类的创建时,我们可以将属性传递给type().__ new__方法,该方法将在返回对象之前在对象上设置,例如

class Foo(type):    
    def __new__(cls, name, bases, attrs):
        attrs['customAttr'] = 'someVal' 
        return type.__new__(cls, name, bases, attrs)

因此:

>> Foo.__dict__
{'customeAttr': 'someVal', ...}

但是我不知道如何对普通(非元)类执行相同的操作,这在使用__setattr__时会导致问题:

class Bar(object):
    def __new__(cls, someVal):
        obj = object().__new__(cls) # cant pass custom attrs
        obj.customAttr = someVal # obj is already a Bar and invokes __setattr__
        return obj

    def __setattr__(*args): raise Exception('read-only class')

不幸的是,

>>> Bar(42)
...
Exception: read-only class

在Bar的__new__中,我从object()返回了一个完整的类实例,并且任何属性访问都通过常规查找规则进行,在这种情况下,调用__setattr__。元类Foo避免了这种情况,因为type()将在低级创建期间在返回实例之前设置属性,而object()不会。

是否存在将属性传递给object()的方法,或者我可以将其用作从__new__返回的实例的另一种类型,该类型允许在属性成为完整类实例之前进行设置吗?我对实例创建后设置__class__这样的解决方案并不感兴趣。

1 个答案:

答案 0 :(得分:1)

您必须通过调用__setattr__或根super object来显式地绕过自己班级的__setattr__。所以你会改变:

obj.customAttr = someVal

收件人:

object.__setattr__(obj, 'customAttr', someVal)

较不通用的方法(不适用于基于__slots__的类)是使用__dict__操作直接分配给dict

obj.__dict__['customAttr'] = someVal  # Equivalently: vars(obj)['customAttr'] = someVal

第一种方法是the newly __slots__-ed uuid.UUID现在使用的方法; before it became __slots__-ed, it used the second approach。在这两种情况下都需要这样做,因为他们使用相同的__setattr__技巧使类型尽可能不变(而不必麻烦将tuplea la typing.NamedTuple子类化)。