__getattr__ for static/class 变量

我有一门课是这样的:

class MyClass:
Foo = 1
Bar = 2

无论何时调用 MyClass.FooMyClass.Bar,我都需要在返回值之前调用一个自定义方法。在巨蟒里有可能吗?我知道如果我创建一个类的实例并定义自己的 __getattr__方法是可能的。但是我的场景涉及使用这个类本身,而不创建它的任何实例。

我还需要一个定制的 __str__方法来调用 str(MyClass.Foo)。 Python 提供这样的选项吗?

38072 次浏览

对象的 __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__()行为。

首先,您需要创建一个元类,并在其上定义 __getattr__()

class MyMetaclass(type):
def __getattr__(self, name):
return '%s result' % name


class MyClass(object):
__metaclass__ = MyMetaclass


print MyClass.Foo

对于第二种情况,no 调用 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的内容对这个解决方案也适用: 它必须在实际返回的类型上实现。

(我知道这是一个古老的问题,但是因为所有其他的答案都使用元类... ...)

您可以使用以下简单的 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

根据具体情况,我使用这种模式

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


LooksLikeAClass = _TheRealClass()

然后导入并使用它。

from foo import LooksLikeAClass
LooksLikeAClass.some_attribute

这样可以避免使用元类,并处理一些用例。