#python #python-3.x #properties #abstract-class
#python #python-3.x #свойства #abstract-class
Вопрос:
Я хочу, чтобы интерпретатор 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 нет встроенного способа сделать это? Должен ли я действительно создавать свой собственный декоратор для обработки такого типа поведения?
Ответ №1:
Вы можете использовать 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
закомментировано!
Комментарии:
1. Спасибо, что ответили альтернативным решением; это было очень проницательно. Поддержано.
Ответ №2:
Вы могли бы поместить проверку времени выполнения в __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
Комментарии:
1. ИМО, это самый простой ответ, поэтому я отмечу как лучший ответ. Спасибо.
Ответ №3:
TLDR — не стоит хлопот, на мой взгляд:
Сочетание linting / mypy и модульных тестов, которые должны удовлетворять большинству ваших потребностей, и небольшие хитрости, связанные с анализом классов / метаклассами, вероятно, не стоят дополнительной когнитивной нагрузки, которую они вносят. Вы «подведете» оформление только один раз, но каждый раз вам придется читать экзотический код scaffolding для того, что вы хотите сделать.
подробности — как помечается фактическое использование?
т. е. if badchild1.name.startswith("John"):
произойдет сбой во время выполнения, и я бы ожидал, что mypy или pylint, например, также будут помечены при анализе, поскольку это будет объект метода. То же самое будет с конкатенацией. Единственными реальными кандидатами на надзор являются f-строки, прямые логические значения или ==
, !=
сравнения на равенство, которым все равно, что это не строка.
в 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)
И запуск кода с двумя новыми строками говорит:
TypeError: can only concatenate str (not "method") to str
Комментарии:
1. Мне действительно нравится этот ответ, но он не всегда применим к людям, которые, возможно, не смогут использовать эти инструменты на работе.
2.@FriskySaga нет проблем. приношу извинения за аспект «не делай этого» в моем ответе, который, как правило, раздражает меня самого в Stackoverflow. хотя я не могу говорить о вашем контексте, если бы я присматривал за кучей пользователей, подключающихся к коду фреймворка, я бы ожидал, что будет больше ошибок, чем просто
@property
отсутствующих декораторов. Моя обработка будет зависеть от а) насколько частыми и постоянными были ошибки, б) сколько сбоев они вызывают — очевидно, если щенок умирает каждый раз, когда забывается @prop, что отличается от 2 минут настройки. Я бы, вероятно, исправил больше с помощью документации / поддержки, чтобы