[ответ написан на основе Python 3.4;синтаксис метакласса отличается в 2, но я думаю, что техника все еще будет работать]
Вы можете сделать это с метаклассом ... в основном.Dappawit почти работает, но я думаю, что у него есть недостаток:
class MetaFoo(type):
@property
def thingy(cls):
return cls._thingy
class Foo(object, metaclass=MetaFoo):
_thingy = 23
Это дает вам свойство класса на Foo, но есть проблема ...
print("Foo.thingy is {}".format(Foo.thingy))
# Foo.thingy is 23
# Yay, the classmethod-property is working as intended!
foo = Foo()
if hasattr(foo, "thingy"):
print("Foo().thingy is {}".format(foo.thingy))
else:
print("Foo instance has no attribute 'thingy'")
# Foo instance has no attribute 'thingy'
# Wha....?
Что, черт возьми, происходит?Здесь?Почему я не могу получить свойство класса из экземпляра?
Я долго бился об этом, прежде чем нашел ответ, который, я считаю, является ответом.Python @properties является подмножеством дескрипторов, и из документации дескриптор (выделено мной):
Поведение по умолчанию для доступа к атрибутам - получить, установить или удалитьатрибут из словаря объекта.Например, a.x
имеет цепочку поиска, начинающуюся с a.__dict__['x']
, затем type(a).__dict__['x']
и продолжающуюся через базовые классы type(a)
, исключая метаклассы .
Такпорядок разрешения метода не включает в себя свойства нашего класса (или что-либо еще, определенное в метаклассе). можно сделать подкласс встроенного декоратора свойств, который ведет себя по-разному, но (необходимо упомянуть) У меня сложилось впечатление, что у разработчиков есть веская причина (чего я не понимаю)за такое.
Это не значит, что нам не повезло;мы можем просто получить доступ к свойствам самого класса ... и мы можем получить класс из type(self)
внутри экземпляра, который мы можем использовать для создания @property dispatchers:
class Foo(object, metaclass=MetaFoo):
_thingy = 23
@property
def thingy(self):
return type(self).thingy
Now Foo().thingy
работает как предназначено для класса и экземпляров!Он также продолжит делать правильные вещи, если производный класс заменит его базовый _thingy
(который изначально использовался мной для этой охоты).
Это не на 100% удовлетворяет меня -- необходимость выполнять настройку как в метаклассе, так и в классе объектов, кажется, что это нарушает принцип СУХОЙ.Но последний - всего лишь одна строка диспетчера;У меня в основном все в порядке, и вы могли бы сжать его до лямбды или чего-то еще, если бы вы действительно этого хотели.