简体   繁体   中英

Can I create class properties during __new__ or __init__?

I want to do something like this, but I haven't had much success so far. I would like to make each attr a property that computes _lazy_eval only when accessed:

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"

** UPDATE **

This doesn't work either:

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

** UPDATE 2 **

Used the __metaclass__ solution, but stuck it in Base.__new__ instead. It looks like it needed a better defined closure -- "prop()" -- to form the property correctly:

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

Descriptors (such as instances of the property type) are only meaningful when they're held in the class object, not the instance object. So, you need to change the class, not the instance, and (in Python 2.6 or better) a class decorator is very handy for that purpose:

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']

If you're stuck with Python 2.5 or earlier, the decorator syntax doesn't apply to classes, but it's easy to get the same effect, just with less nifty syntax -- change the last 3 rows to:

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

which has the same semantics as the class decorator syntax.

Technically you want a metaclass:

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

您可以通过__ class __ dict访问类属性

self.__class__.attr

You could consider using __getattr__() instead:

class Base(object):
    def __getattr__(self, attr):
        if attr not in self._myattrs:
            raise AttributeError
        return 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM