如何在不忘记在子 class 中添加 属性 装饰器的情况下覆盖抽象 属性 方法?

How to override an abstract property method without forgetting to add the property decorator in the child class?

我希望 Python 解释器在我覆盖抽象 属性 方法时对我大喊大叫,但忘记在子 [=20] 中指定它仍然是 属性 方法=].

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 真的没有内置的方法来做到这一点吗?我真的必须创建自己的装饰器来处理这种行为吗?

您可以在 Parent 的 __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

您可以使用 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, name, bases, 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 被注释掉时!

TLDR - 在我看来不值得麻烦:

介于 linting/mypy 和单元测试之间,应该涵盖您的大部分需求和围绕 class analysis/metaclasses 的小技巧可能不值得它们带来额外的认知负担。您将你的装饰只会“失败”一次,但每次都必须阅读异国情调的脚手架代码才能完成你想做的事情。

详细信息 - 实际使用被标记为什么?

if badchild1.name.startswith("John"): 将在运行时失败,我希望 mypy 或 pylint 也能标记分析,因为它将成为一个方法对象。串联也是如此。唯一真正需要监督的候选人是 f-strings、直接布尔值或 ==!= 不关心它不是字符串的相等比较。

pylint 有话要说:

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)

并且 运行 带有 2 行新行的代码表示:

TypeError: can only concatenate str (not "method") to str