__getattr__ для переменных static/class в python
У меня есть класс вроде:
class MyClass:
Foo = 1
Bar = 2
Всякий раз, когда вызывается MyClass.Foo
или MyClass.Bar
, мне нужен специальный метод, который нужно вызвать до того, как будет возвращено значение. Возможно ли это в Python? Я знаю, что это возможно, если я создаю экземпляр класса, и я могу определить свой собственный метод __getattr__
. Но мой scnenario предполагает использование этого класса как такового, не создавая его.
Также мне нужен настраиваемый метод __str__
, который вызывается при вызове str(MyClass.Foo)
. Предоставляет ли Python такую возможность?
Ответы
Ответ 1
__getattr__()
и __str__()
для объекта находятся в его классе, поэтому, если вы хотите настроить эти вещи для класса, вам нужен класс класса. Метакласс.
class FooType(type):
def _foo_func(cls):
return 'foo!'
def _bar_func(cls):
return 'bar!'
def __getattr__(cls, key):
if key == 'Foo':
return cls._foo_func()
elif key == 'Bar':
return cls._bar_func()
raise AttributeError(key)
def __str__(cls):
return 'custom str for %s' % (cls.__name__,)
class MyClass:
__metaclass__ = FooType
# in python 3:
# class MyClass(metaclass=FooType):
# pass
print MyClass.Foo
print MyClass.Bar
print str(MyClass)
печать:
foo!
bar!
custom str for MyClass
И нет, объект не может перехватить запрос на приведение в соответствие одного из его атрибутов. Объект, возвращаемый для атрибута, должен определять свое собственное __str__()
.
Ответ 2
(Я знаю, что это старый вопрос, но поскольку все остальные ответы используют метакласс...)
Вы можете использовать следующий простой дескриптор classproperty
:
class classproperty(object):
""" @[email protected] """
def __init__(self, f):
self.f = classmethod(f)
def __get__(self, *a):
return self.f.__get__(*a)()
Используйте его как:
class MyClass(object):
@classproperty
def Foo(cls):
do_something()
return 1
@classproperty
def Bar(cls):
do_something_else()
return 2
Ответ 3
Для первого вам нужно создать метакласс и определить __getattr__()
.
class MyMetaclass(type):
def __getattr__(self, name):
return '%s result' % name
class MyClass(object):
__metaclass__ = MyMetaclass
print MyClass.Foo
Для второго нет. Вызов str(MyClass.Foo)
вызывает MyClass.Foo.__str__()
, поэтому вам нужно вернуть соответствующий тип для MyClass.Foo
.
Ответ 4
Удивлен, никто не указал на это:
class FooType(type):
@property
def Foo(cls):
return "foo!"
@property
def Bar(cls):
return "bar!"
class MyClass(metaclass=FooType):
pass
Работает:
>>> MyClass.Foo
'foo!'
>>> MyClass.Bar
'bar!'
(для Python 2.x измените определение MyClass
на:
class MyClass(object):
__metaclass__ = FooType
)
То, что другие ответы говорят о str
справедливо для этого решения: оно должно быть реализовано для фактически возвращаемого типа.
Ответ 5
В зависимости от случая я использую этот шаблон
class _TheRealClass:
def __getattr__(self, attr):
pass
LooksLikeAClass = _TheRealClass()
Затем вы импортируете и используете его.
from foo import LooksLikeAClass
LooksLikeAClass.some_attribute
Это позволяет избежать использования метакласса и обрабатывать некоторые варианты использования.