我可以在 __new__ 或 __init__ 中创建类属性吗?

5 投票
4 回答
1913 浏览
提问于 2025-04-15 20:43

我想做类似的事情,但到目前为止还没有成功。我希望每个属性在被访问时才计算一次 _lazy_eval:

class Base(object):
    def __init__(self):
        for attr in self._myattrs:
            setattr(self, attr, property(lambda self: self._lazy_eval(attr)))

    def _lazy_eval(self, attr):
        #Do complex stuff here
        return attr


class Child(Base):
    _myattrs = ['foo', 'bar']


me = Child()
print me.foo
print me.bar

#desired output:
#"foo"
#"bar"

** 更新 **

这个方法也不行:

class Base(object):
    def __new__(cls):
        for attr in cls._myattrs:
            setattr(cls, attr, property(lambda self: self._lazy_eval(attr)))
        return object.__new__(cls)

#Actual output (it sets both .foo and .bar equal to "bar"??)
#bar
#bar

** 更新 2 **

我用了 __metaclass__ 的方法,但把它放在了 Base.__new__ 里。看起来需要一个更明确的闭包——“prop()”——来正确地形成属性:

class Base(object):
    def __new__(cls):
        def prop(x):
            return property(lambda self: self._lazy_eval(x))
        for attr in cls._myattrs:
            setattr(cls, attr, prop(attr))
        return object.__new__(cls)

#Actual output!  It works!
#foo
#bar

4 个回答

1

你可以通过 __ class __ dict 来访问类的属性。

self.__class__.attr
5

描述符(比如 property 类型的实例)只有在它们被放在 对象里时才有意义,而不是放在 实例 对象里。所以,你需要修改的是类,而不是实例。在 Python 2.6 或更高版本中,使用类装饰器来实现这一点非常方便:

class Base(object):
    def _lazy_eval(self, attr):
        #Do complex stuff here
        return attr

def lazyclass(cls):
    for attr in cls._myattrs:
        setattr(cls, attr, property(lambda self: self._lazy_eval(attr)))
    return cls

@lazyclass
class Child(Base):
    _myattrs = ['foo', 'bar']

如果你还在用 Python 2.5 或更早的版本,类装饰器的语法就不适用了,但你仍然可以用其他简单的方法达到同样的效果,只是语法看起来没那么炫酷——把最后三行改成:

class Child(Base):
    _myattrs = ['foo', 'bar']
Child = lazyclass(Child)

这样做的效果和类装饰器的语法是一样的。

1

从技术上讲,你需要一个元类:

class LazyMeta(type):
    def __init__(cls, name, bases, attr):
        super(LazyMeta, cls).__init__(name, bases, attr)
        def prop( x ):
            return property(lambda self: self._lazy_eval(x))
        for x in attr['lazyattrs']:
            setattr(cls, x, prop(x))

class Base(object):
    __metaclass__ = LazyMeta
    lazyattrs = []
    def _lazy_eval(self, attr):
        #Do complex stuff here
        return attr

class Child(Base):
    lazyattrs = ['foo', 'bar']

me = Child()

print me.foo
print me.bar

撰写回答