__name__ 上的摘要 属性 未强制执行
Abstract Property on __name__ not enforced
考虑以下示例代码:
from abc import ABC, abstractmethod, abstractproperty
class Base(ABC):
@abstractmethod
def foo(self) -> str:
print("abstract")
@property
@abstractmethod
def __name__(self) -> str:
return "abstract"
@abstractmethod
def __str__(self) -> str:
return "abstract"
@property
@abstractmethod
def __add__(self, other) -> str:
return "abstract"
class Sub(Base):
def foo(self):
print("concrete")
def __str__(self):
return "concrete"
def __add__(self, other) -> str:
return "concrete"
sub = Sub()
sub.foo()
sub.__name__
print(str(sub))
请注意,子类没有实现抽象 属性 __name__
,实际上当 __name__
被引用时,它从其父类打印为 "abstract":
>>> sub.foo()
concrete
>>> sub.__name__
'abstract'
>>> print(str(sub))
concrete
然而,这不是因为 __name__
是一个 dunder 方法,也不是因为 @property
和 @abstractmethod
装饰器不能很好地协同工作,因为如果我删除实现来自 Sub
的 __add__
,它不允许我实例化它。 (我知道 __add__
通常不是 属性,但我想使用 'real' dunder 方法)如果我删除 __str__
和 foo
。只有 __name__
会这样。
__name__
是什么导致了这种行为?有什么办法解决这个问题,还是我需要让父(抽象)实现手动为我提高 TypeError
?
类 通过 数据描述符 在 type
:
上具有 __name__
属性
>>> Sub.__name__
'Sub'
>>> '__name__' in Sub.__dict__
False
它是一个数据描述符,因为它还拦截赋值以确保该值是一个字符串。实际值存储在 C 结构的槽中,描述符是该值的代理(因此在 class 上设置新值也不会向 __dict__
添加新条目) :
>>> Sub.__name__ = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign string to NewName.__name__, not 'NoneType'
>>> Sub.__name__ = 'NewName'
>>> Sub.__name__
'NewName'
>>> '__name__' in Sub.__dict__
False
(实际上访问该描述符而不触发它的 __get__
是不可能的,因为 type
本身没有 __dict__
并且本身有一个 __name__
)。
这导致在创建Sub
实例时对该属性的测试成功,class毕竟有该属性:
>>> hasattr(Sub, '__name__')
True
在 Sub
的实例上,然后找到 Base.__name__
实现,因为实例描述符规则仅考虑 class 和基础 classes,而不考虑元类型。
考虑以下示例代码:
from abc import ABC, abstractmethod, abstractproperty
class Base(ABC):
@abstractmethod
def foo(self) -> str:
print("abstract")
@property
@abstractmethod
def __name__(self) -> str:
return "abstract"
@abstractmethod
def __str__(self) -> str:
return "abstract"
@property
@abstractmethod
def __add__(self, other) -> str:
return "abstract"
class Sub(Base):
def foo(self):
print("concrete")
def __str__(self):
return "concrete"
def __add__(self, other) -> str:
return "concrete"
sub = Sub()
sub.foo()
sub.__name__
print(str(sub))
请注意,子类没有实现抽象 属性 __name__
,实际上当 __name__
被引用时,它从其父类打印为 "abstract":
>>> sub.foo()
concrete
>>> sub.__name__
'abstract'
>>> print(str(sub))
concrete
然而,这不是因为 __name__
是一个 dunder 方法,也不是因为 @property
和 @abstractmethod
装饰器不能很好地协同工作,因为如果我删除实现来自 Sub
的 __add__
,它不允许我实例化它。 (我知道 __add__
通常不是 属性,但我想使用 'real' dunder 方法)如果我删除 __str__
和 foo
。只有 __name__
会这样。
__name__
是什么导致了这种行为?有什么办法解决这个问题,还是我需要让父(抽象)实现手动为我提高 TypeError
?
类 通过 数据描述符 在 type
:
__name__
属性
>>> Sub.__name__
'Sub'
>>> '__name__' in Sub.__dict__
False
它是一个数据描述符,因为它还拦截赋值以确保该值是一个字符串。实际值存储在 C 结构的槽中,描述符是该值的代理(因此在 class 上设置新值也不会向 __dict__
添加新条目) :
>>> Sub.__name__ = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only assign string to NewName.__name__, not 'NoneType'
>>> Sub.__name__ = 'NewName'
>>> Sub.__name__
'NewName'
>>> '__name__' in Sub.__dict__
False
(实际上访问该描述符而不触发它的 __get__
是不可能的,因为 type
本身没有 __dict__
并且本身有一个 __name__
)。
这导致在创建Sub
实例时对该属性的测试成功,class毕竟有该属性:
>>> hasattr(Sub, '__name__')
True
在 Sub
的实例上,然后找到 Base.__name__
实现,因为实例描述符规则仅考虑 class 和基础 classes,而不考虑元类型。