Python design - initializing, setting, and getting class attributes

Question

I have a class in which a method first needs to verify that an attribute is present and otherwise call a function to compute it. Then, ensuring that the attribute is not None, it performs some operations with it. I can see two slightly different design choices:

class myclass():
    def __init__(self):
        self.attr = None

    def compute_attribute(self):
        self.attr = 1

    def print_attribute(self):
        if self.attr is None:
            self.compute_attribute()
        print self.attr

And

class myclass2():
    def __init__(self):
        pass

    def compute_attribute(self):
        self.attr = 1
        return self.attr

    def print_attribute(self):
        try:
            attr = self.attr
        except AttributeError:
            attr = self.compute_attribute()
        if attr is not None:
            print attr

In the first design, I need to make sure that all the class attributes are set to None in advance, which can become verbose but also clarify the structure of the object.

The second choice seems to be the more widely used one. However, for my purposes (scientific computing related to information theory) using try except blocks everywhere can be a bit of an overkill given that this class doesn't really interact with other classes, it just takes data and computes a bunch of things.


Show source
| class   | python   | oop   | scientific-computing   | class-attributes   2017-01-07 14:01 2 Answers

Answers to Python design - initializing, setting, and getting class attributes ( 2 )

  1. 2017-01-07 15:01

    Based on the answer jonrsharpe linked, I offer a third design choice. The idea here is that no special conditional logic is required at all either by the clients of MyClass or by code within MyClass itself. Instead, a decorator is applied to a function that does the (hypothetically expensive) computation of the property, and then that result is stored.

    This means that the expensive computation is done lazily (only if a client tries to access the property) and only performed once.

    def lazyprop(fn):
        attr_name = '_lazy_' + fn.__name__
    
        @property
        def _lazyprop(self):
            if not hasattr(self, attr_name):
                setattr(self, attr_name, fn(self))
            return getattr(self, attr_name)
    
        return _lazyprop
    
    
    class MyClass(object):
        @lazyprop
        def attr(self):
            print('Generating attr')
            return 1
    
        def __repr__(self):
            return str(self.attr)
    
    
    if __name__ == '__main__':
        o = MyClass()
        print(o.__dict__, end='\n\n')
        print(o, end='\n\n')
        print(o.__dict__, end='\n\n')
        print(o)
    

    Output

    {}
    
    Generating attr
    1
    
    {'_lazy_attr': 1}
    
    1
    

    Edit

    Application of Cyclone's answer to OP's context:

    class lazy_property(object):
        '''
        meant to be used for lazy evaluation of an object attribute.
        property should represent non-mutable data, as it replaces itself.
        '''
    
        def __init__(self, fget):
            self.fget = fget
            self.func_name = fget.__name__
    
        def __get__(self, obj, cls):
            if obj is None:
                return None
            value = self.fget(obj)
            setattr(obj, self.func_name, value)
            return value
    
    
    class MyClass(object):
        @lazy_property
        def attr(self):
            print('Generating attr')
            return 1
    
        def __repr__(self):
            return str(self.attr)
    
    
    if __name__ == '__main__':
        o = MyClass()
        print(o.__dict__, end='\n\n')
        print(o, end='\n\n')
        print(o.__dict__, end='\n\n')
        print(o)
    

    The output is identical to above.

  2. 2017-01-07 15:01

    Firstly, you can use hasattr to check if an object has an attribute, it returns True if the attribute exists.

    hasattr(object, attribute) # will return True if the object has the attribute
    

    Secondly, You can customise attribute access in Python, you can read more about it here: https://docs.python.org/2/reference/datamodel.html#customizing-attribute-access

    Basically, you override the __getattr__ method to achieve this, so something like:

    class myclass2(): def init(self): pass

    def compute_attr(self):
        self.attr = 1
        return self.attr
    
    def print_attribute(self):
        print self.attr
    
    def __getattr__(self, name):
        if hasattr(self, name) and getattr(self, name)!=None:
            return getattr(self, name):
        else:
            compute_method="compute_"+name; 
            if hasattr(self, compute_method):
                return getattr(self, compute_method)()
    

    Make sure you only use getattr to access the attribute within __getattr__ or you'll end up with infinite recursion

Leave a reply to - Python design - initializing, setting, and getting class attributes

◀ Go back