Can I create class properties during __new__ or __init__?

I want to do something similar, but so far I have not had much success. I would like to make each attr a property that _lazy_eval only evaluates on access:

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 also does not work:

 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 instead inserted it into Base.__new__ . It seems that to correctly define the property, it was necessary to define a clearer closure - "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
source share
4 answers

Technically, you need 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 
+1
source

Descriptors (for example, instances of type property ) make sense only when they are stored in the class object, and not in the instance. So, you need to change the class, not the instance, and (in Python 2.6 or better) the class decor is very convenient for this:

 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 are stuck with Python 2.5 or earlier, the decorator syntax does not apply to classes, but it is easy to get the same effect, only with less powerful syntax - change the last 3 lines to:

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

which has the same semantics as the class decorator syntax.

+5
source

You can access class attributes through class __ __ dict

 self.__class__.attr 
0
source

Instead, you can use __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
source

Source: https://habr.com/ru/post/1304829/


All Articles