Могу ли я создать свойства класса во время __new__ или __init__? - PullRequest
4 голосов
/ 23 марта 2010

Я хочу сделать что-то подобное, но пока не достиг большого успеха. Я хотел бы сделать каждый атрибут атрибутом, который вычисляет _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 ]

5 голосов
/ 23 марта 2010

Дескрипторы (например, экземпляры типа 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)

, которая имеет ту же семантику, что и синтаксис декоратора класса.

1 голос
/ 23 марта 2010

Технически вы хотите метакласс:

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
0 голосов
/ 23 марта 2010

Вы можете использовать __getattr__() вместо:

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
0 голосов
/ 23 марта 2010

Вы можете получить доступ к атрибутам класса через __ class __ dict

self.__class__.attr
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...