问题描述
如果我重写一个抽象属性方法,我希望Python解释器对我大吼大叫,但是忘记指定它仍然是子类中的一个属性方法。
class Parent(Metaclass=ABCMeta):
@property
@abstractmethod
def name(self) -> str:
pass
class Child(Parent):
@property # If I forget this,I want Python to yell at me.
def name(self) -> str:
return 'The Name'
if __name__ == '__main__':
print(Child().name)
Python是否真的没有内置的方法来执行此操作?我必须真的创建自己的装饰器来处理这种行为吗?
解决方法
您可以使用metaclass
:
class Parent(type):
def __new__(cls,name,bases,body):
if 'name' not in body.keys() or body['name'].__class__.__name__ != 'property':
raise TypeError(f"Can't instantiate class {name} without property name")
return super().__new__(cls,body)
class Child(metaclass=Parent):
@property # If I forget this,raise TypeError
def name(self) -> str: # also,name must be implemented
return 'The Name'
if __name__ == '__main__':
print(Child().name)
当TypeError: Can't instantiate class Child without property name
被注释掉时,将引发@property
!
您可以将运行时检查放入父级的__init__
方法中,如果name
是方法,则引发异常。
class Parent(metaclass=ABCMeta):
def __init__(self):
assert not callable(self.name)
@abstractmethod
def name(self) -> str:
pass
class GoodChild(Parent):
@property
def name(self) -> str:
return 'The Name'
class BadChild(Parent):
def name(self) -> str:
return 'Whoops,not a property'
if __name__ == '__main__':
good_child = GoodChild()
print(good_child.name) # Prints 'The Name'
bad_child = BadChild() # Raises an AssertionError when initialized
,
TLDR-我认为这不值得麻烦:
在linting / mypy和单元测试之间应该可以满足您的大多数需求,而围绕类分析/元类的小技巧可能不值得它们带来额外的认知负担。您只会“失败”一次装饰,但是会必须每次都要阅读异国脚手架代码。
详细信息-实际用途被标记为什么?
即if badchild1.name.startswith("John"):
将在运行时失败,并且我希望mypy或pylint也可以标记分析,因为它将成为方法对象。串联也是如此。唯一真正的监督候选者是f字符串,直接的布尔值或==
,!=
等式比较,它们并不关心它不是字符串。
圆筒的意思是:
test_171_prop.py:11 Method 'name' was expected to be 'property',found it instead as 'method' (invalid-overridden-method)
mypy 没有问题。
但是,如果我添加此内容:
child = Child()
print("name:" + child.name)
然后 mypy 说:
test_171_prop.py:16: error: Unsupported operand types for + ("str" and "Callable[[],str]")
Found 1 error in 1 file (checked 1 source file)
然后用两行新代码运行代码:
TypeError: can only concatenate str (not "method") to str