6 python setattr python-descriptors
这个问题可能在某个地方有答案,但我找不到。
我用来setattr向类添加一长串描述符,并且我发现__set_name__当我使用setattr.
这对我来说不是一个大问题,因为我可以通过设置描述符的名称来代替__init__,我只是想了解为什么__set_name__不被调用,以及是否是因为我做错了什么。
class MyDescriptor:
def __set_name__(self, owner, name):
self._owner = owner
self._name = name
def __get__(self, instance, owner):
return self._name
class MyClass:
a = MyDescriptor()
setattr(MyClass, 'b', MyDescriptor()) # dynamically adding descriptor, __set_name__ is not called!
mc = MyClass()
print(mc.a) # prints 'a'
print(mc.b) # raises AttributeError: 'MyDescriptor' object has no attribute '_name'
Run Code Online (Sandbox Code Playgroud)
__set_name__在函数中创建类时为每个描述符调用type.__new__:它不是“反应式协议”,也就是说,每当创建描述符时都会触发。
解决这个问题的简单方法是__set_name__在调用 setattr 之后手动调用:
descr_name = 'b'
setattr(MyClass, descr_name, MyDescriptor())
getattr(MyClass, 'b').__set_name__(MyClass, descr_name)
Run Code Online (Sandbox Code Playgroud)
尽管可以有一个带有自定义__setattr__方法的元类来为您执行此操作。__setattr__类中的方法实现是 Python 向属性设置添加反应行为的方式 - 只是这次您需要类中的行为,而不是实例中的行为。
如果您在很多地方都这样做,并且确实希望它是透明的,那么可能需要这样做:
class Meta(type):
def __setattr__(cls, attr, value):
super().__setattr__(attr, value)
if (setname:=getattr(value, "__set_name__", None) ) and callable(setname):
setname(cls, attr)
class MyClass(metaclass=Meta):
a = MyDescriptor()
Run Code Online (Sandbox Code Playgroud)
现在,在交互式提示上检查它:
In [83]: setattr(MyClass, "b", MyDescriptor())
In [84]: MyClass.b
Out[84]: 'b'
In [85]: MyClass.__dict__["b"]._name
Out[85]: 'b'
Run Code Online (Sandbox Code Playgroud)