__set_name__ 在描述符中执行
__set_name__ execution in Descriptor
我发现了一个包含描述符的代码。据我了解, __set_name__
是在创建 class 时调用的方法。然后,如果 class 被调用两次,我会接到两个调用。
在下面的代码片段中,我希望在 __set_name__
中接到两次电话,但我只接到了一次电话。为什么会出现这种行为?
class SharedAttribute:
def __init__(self, initial_value=None):
self.value = initial_value
self._name = None
def __get__(self, instance, owner):
if instance is None:
return self
if self.value is None:
raise AttributeError(f'{self._name} was never set')
return self.value
def __set__(self, instance, new_value):
self.value = new_value
def __set_name__(self, owner, name):
print(f'{self} was named {name} by {owner}')
self._name = name
class GitFetcher:
current_tag = SharedAttribute()
current_branch = SharedAttribute()
def __init__(self, tag, branch=None):
self.current_tag = tag
self.current_branch = branch
@property
def current_tag(self):
if self._current_tag is None:
raise AttributeError("tag was never set")
return self._current_tag
@current_tag.setter
def current_tag(self, new_tag):
self.__class__._current_tag = new_tag
def pull(self):
print(f"pulling from {self.current_tag}")
return self.current_tag
f1 = GitFetcher(0.1)
f2 = GitFetcher(0.2)
f1.current_tag = 0.3
f2.pull()
f1.pull()
在之前的执行过程中,__set_name__
被current_branch调用,但没有被current_tag调用。为什么会有这种区别?唯一的调用是这个:
<__main__.SharedAttribute object at 0x047BACB0> was named current_branch by <class '__main__.GitFetcher'>
TL;DR 在调用 __set_name__
方法时,current_tag
指的是 property
的实例,而不是 SharedAttribute
.[=30= 的实例]
__set_name__
在 之后被调用 class 已经被定义(这样 class 可以作为 owner
参数),而不是在赋值后立即进行。
但是,您将 current_tag
的值更改为 property
,因此一旦 class 定义具有,该名称就不再绑定到 SharedAttribute
实例完成。'
来自 documentation(强调我的):
Automatically called at the time the owning class owner is created.
SharedAttribute
实例是在执行 class
语句的主体时创建的。 class 本身直到 在 主体执行后才会创建;执行正文的结果是一个命名空间,它作为参数传递给创建 class 的 metaclass。在此过程中,使用 __set_name__
方法扫描 class 属性的值,只有 then 是调用的方法。
这是一个更简单的例子:
class GitFetcher:
current_branch = SharedAttribute()
current_tag = SharedAttribute()
current_tag = 3
在定义 GitFetcher
时,current_tag
不再绑定到描述符,因此不会尝试调用 current_tag.__set_name__
。
尚不清楚您是否想以某种方式将 属性 与 SharedAttribute
组合在一起,或者这是否只是名称 current_tag
的无意 re-use。
我发现了一个包含描述符的代码。据我了解, __set_name__
是在创建 class 时调用的方法。然后,如果 class 被调用两次,我会接到两个调用。
在下面的代码片段中,我希望在 __set_name__
中接到两次电话,但我只接到了一次电话。为什么会出现这种行为?
class SharedAttribute:
def __init__(self, initial_value=None):
self.value = initial_value
self._name = None
def __get__(self, instance, owner):
if instance is None:
return self
if self.value is None:
raise AttributeError(f'{self._name} was never set')
return self.value
def __set__(self, instance, new_value):
self.value = new_value
def __set_name__(self, owner, name):
print(f'{self} was named {name} by {owner}')
self._name = name
class GitFetcher:
current_tag = SharedAttribute()
current_branch = SharedAttribute()
def __init__(self, tag, branch=None):
self.current_tag = tag
self.current_branch = branch
@property
def current_tag(self):
if self._current_tag is None:
raise AttributeError("tag was never set")
return self._current_tag
@current_tag.setter
def current_tag(self, new_tag):
self.__class__._current_tag = new_tag
def pull(self):
print(f"pulling from {self.current_tag}")
return self.current_tag
f1 = GitFetcher(0.1)
f2 = GitFetcher(0.2)
f1.current_tag = 0.3
f2.pull()
f1.pull()
在之前的执行过程中,__set_name__
被current_branch调用,但没有被current_tag调用。为什么会有这种区别?唯一的调用是这个:
<__main__.SharedAttribute object at 0x047BACB0> was named current_branch by <class '__main__.GitFetcher'>
TL;DR 在调用 __set_name__
方法时,current_tag
指的是 property
的实例,而不是 SharedAttribute
.[=30= 的实例]
__set_name__
在 之后被调用 class 已经被定义(这样 class 可以作为 owner
参数),而不是在赋值后立即进行。
但是,您将 current_tag
的值更改为 property
,因此一旦 class 定义具有,该名称就不再绑定到 SharedAttribute
实例完成。'
来自 documentation(强调我的):
Automatically called at the time the owning class owner is created.
SharedAttribute
实例是在执行 class
语句的主体时创建的。 class 本身直到 在 主体执行后才会创建;执行正文的结果是一个命名空间,它作为参数传递给创建 class 的 metaclass。在此过程中,使用 __set_name__
方法扫描 class 属性的值,只有 then 是调用的方法。
这是一个更简单的例子:
class GitFetcher:
current_branch = SharedAttribute()
current_tag = SharedAttribute()
current_tag = 3
在定义 GitFetcher
时,current_tag
不再绑定到描述符,因此不会尝试调用 current_tag.__set_name__
。
尚不清楚您是否想以某种方式将 属性 与 SharedAttribute
组合在一起,或者这是否只是名称 current_tag
的无意 re-use。