Свойство не является атрибутом экземпляра, это атрибут дескриптор класса, который привязан к объекту, который получил к нему доступ.Вот упрощенный пример:
class A:
@property
def x(self):
return True
print(A.x) # <property object at 0x0000021AE2944548>
print(A().x) # True
Получая A.x
, вы получаете несвязанное свойство.При получении A().x
свойство привязывается к экземпляру, а A.x.__get__
возвращает значение.
Назад к вашему коду
Это означает, что свойство должно бытьатрибут класса , а не атрибут экземпляра.Или, в вашем случае, свойство должно быть атрибутом metaclass .
class Meta(type):
@property
def abstract(cls):
return getattr(cls, '_abstract', False)
@abstract.setter
def abstract(cls, value):
if value in (True, False):
setattr(cls, '_abstract', value)
else:
raise TypeError(f'invalid abstract assignment {value}')
class Foo(metaclass=Meta):
pass
print(Foo.abstract) # False
Foo.abstract = 'Not at bool' # TypeError: invalid abstract assignment Not at bool
Хотя это только частично решает вашу проблему, так как вы хотите, чтобы у каждого метода ваших классов был abstract
имущество.Для этого вам понадобится их класс , чтобы иметь это property
.
Прежде чем мы углубимся, позвольте мне напомнить вам одну ключевую концепцию в Python: we 'Здесь все взрослые по согласию .Может быть, вы должны просто доверить своим пользователям, чтобы они не устанавливали abstract
на что-либо другое, кроме bool
, и позволяли ему быть атрибутом, не относящимся к свойствуВ частности, любой пользователь может изменить сам атрибут _abstract
в любом случае.
Если это вас не устраивает и вы хотите, чтобы абстрактный был property
, то один из способов сделать это - определить класс-оболочкудля методов, которые удерживают это property
.
class Abstract:
@property
def abstract(cls):
return getattr(cls, '_abstract', False)
@abstract.setter
def abstract(cls, value):
if value in (True, False):
setattr(cls, '_abstract', value)
else:
raise TypeError(f'invalid abstract assignment {value}')
class AbstractCallable(Abstract):
def __init__(self, f):
self.callable = f
def __call__(self, *args, **kwargs):
return self.callable(*args, **kwargs)
class Meta(type, Abstract):
def __new__(mcls, name, bases, dct, **kwargs):
for name, value in dct.items():
if callable(value):
dct[name] = AbstractCallable(value)
return super().__new__(mcls, name, bases, dct)
class Foo(metaclass=Meta):
def bar(self):
pass
print(Foo.abstract) # False
print(Foo.bar.abstract) # False
Foo.bar.abstract = 'baz' # TypeError: invalid abstract assignment baz