getattr для переменных static/class в python

у меня есть класс типа:

class MyClass:
     Foo = 1
     Bar = 2

, когда MyClass.Foo или MyClass.Bar вызывается, мне нужен пользовательский метод, который будет вызван до возвращения значения. Возможно ли это в Python? Я знаю, что это возможно, если я создаю экземпляр класса и я могу определить свой собственный __getattr__ метод. Но мой scnenario включает в себя использование этого класса как такового без создания его экземпляра.

также мне нужен таможни __str__ метод, который будет вызван, когда str(MyClass.Foo) вызывается. Предоставляет ли Python такой вариант?

5 ответов


__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__() поведение.


(Я знаю, что это старый вопрос, но так как все остальные ответы используют метакласс...)

вы можете использовать следующие простые classproperty дескриптор:

class classproperty(object):
    """ @classmethod+@property """
    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

для первого Вам нужно создать метакласс и определить __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.


удивлен, никто не указал на это:

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 имеет значение true для этого решения: оно должно быть реализовано на фактически возвращаемом типе.


в зависимости от ситуации я использую этот шаблон

class _TheRealClass:
    def __getattr__(self, attr):
       pass

LooksLikeAClass = _TheRealClass()

затем вы импортируете и используете его.

from foo import LooksLikeAClass
LooksLikeAClass.some_attribute

это позволяет избежать использования метакласса и обрабатывать некоторые варианты использования.