【问题标题】:Can I create class properties during __new__ or __init__?我可以在 __new__ 或 __init__ 期间创建类属性吗?
【发布时间】:2011-01-30 14:45:51
【问题描述】:

我想做这样的事情,但到目前为止我还没有取得太大的成功。我想让每个 attr 成为仅在访问时计算 _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

【问题讨论】:

    标签: python


    【解决方案1】:

    描述符(例如property 类型的实例)仅在它们被保存在class 对象而不是instance 对象中时才有意义。因此,您需要更改类,而不是实例,并且(在 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 或更早版本,则装饰器语法不适用于类,但很容易获得相同的效果,只是语法不那么漂亮 - 将最后 3 行更改为:

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

    与类装饰器语法具有相同的语义。

    【讨论】:

    • 谢谢,亚历克斯。不幸的是,这种装饰器方法对我不起作用。它给了我与 new 方法相同的结果(请参阅我对上述问题的更新)。
    【解决方案2】:

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

    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
    

    【讨论】:

      【解决方案3】:

      可以通过__class __dict访问类属性

      self.__class__.attr
      

      【讨论】:

        【解决方案4】:

        您可以考虑改用__getattr__()

        类基(对象): def __getattr__(self, attr): 如果 attr 不在 self._myattrs 中: 引发属性错误 返回 self._lazy_eval(attr) def _lazy_eval(self, attr): #在这里做复杂的事情 返回属性 儿童类(基础): _myattrs = ['foo', 'bar'] 我=孩子() 打印我.foo 打印我.bar

        【讨论】:

        • 谢谢!这行得通。感觉有点不优雅,但它可能是最好的解决方案。
        猜你喜欢
        • 2019-06-18
        • 2013-04-03
        • 2011-10-27
        • 2012-01-01
        • 2010-11-03
        • 2013-07-05
        • 1970-01-01
        • 2016-03-19
        • 1970-01-01
        相关资源
        最近更新 更多