web-dev-qa-db-ja.com

pythonの静的/クラス変数の__getattr__

私は次のようなクラスを持っています:

_class MyClass:
     Foo = 1
     Bar = 2
_

_MyClass.Foo_または_MyClass.Bar_が呼び出されるたびに、値が返される前にカスタムメソッドを呼び出す必要があります。 Pythonでは可能ですか?クラスのインスタンスを作成し、独自の___getattr___メソッドを定義できる場合は可能です。しかし、私のシナリオでは、このクラスをインスタンスを作成せずにそのまま使用します。

また、str(MyClass.Foo)が呼び出されたときに呼び出されるカスタム___str___メソッドが必要です。 Pythonはそのようなオプションを提供しますか?

59
Tuxdude

オブジェクトの__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
_

いいえ、オブジェクトは、その属性の1つを文字列化するリクエストを傍受することはできません。属性に対して返されるオブジェクトは、独自の__str__()動作を定義する必要があります。

65
Matt Anderson

(私はこれが古い質問であることを知っていますが、他のすべての回答はメタクラスを使用するため...)

次の単純な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
15
shx2

最初に、メタクラスを作成し、その上に__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について他の回答が言うことは、このソリューションにも当てはまります。実際に返される型に実装する必要があります。

6
Jonas Schäfer

場合によっては、このパターンを使用します

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

LooksLikeAClass = _TheRealClass()

次に、それをインポートして使用します。

from foo import LooksLikeAClass
LooksLikeAClass.some_attribute

これにより、メタクラスの使用が回避され、いくつかのユースケースが処理されます。

0
geckos