Python design - initializing, setting, and getting class attributes - python

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.

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

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.

Related

Does __dict__ exist or not in Python?

The first code snippet:
class A:
def __init__(self):
print(self.__dict__)
def __getattr__(self, name):
print("get")
def __setattr__(self, name, value):
print("set")
# def __getattribute__(self, name):
# print("getatrr")
a = A()
It prints {} and the function __getattr__ isn't invoked, which means the attribute__dict__ exists.
The second snippet:
class A:
def __init__(self):
print(self.__dict__)
def __getattr__(self, name):
print("get")
def __setattr__(self, name, value):
print("set")
def __getattribute__(self, name):
print("getatrr")
a = A()
It prints getatrr and None, which means the attribute __dict__ doesn't exist.
Why is __dict__ {} in the first case, but None in the second case?
the issue is that when you define this:
def __getattribute__(self, name):
print("getatrr")
you're overriding __getattribute__ which is supposed to return something. Since you're not returning anything, you get None for every attribute you'll try.
Documentation states:
This method should return the (computed) attribute value or raise an AttributeError exception
A viable way to define it is to call object.__getattribute__ in the fallback case (in my example, I have added a small test on __dict__ which prints:
def __getattribute__(self, name):
if name == "__dict__":
print("get attribute invoked with __dict__")
return object.__getattribute__(self,name)
In the end, the hard attribute lookup work is done with object.__getattribute__ that invokes python runtime.

Python Object Wrapper

I am trying to create a wrapper class that behaves almost like the wrapped object. So far, I have come up with the following code:
import functools
import types
method_wrapper = type((None).__str__)
class Box:
def __new__(cls, obj):
attrs = {}
attr_names = dir(obj)
for attr_name in attr_names:
attr = obj.__getattribute__(attr_name)
if isinstance(attr, (types.MethodType, method_wrapper)):
"Attr is a bound method, ignore self"
#functools.wraps(attr)
def wrapped_attr(self, *args, **kwargs):
return attr(*args, **kwargs)
attrs[attr_name] = wrapped_attr
elif isinstance(attr, types.FunctionType):
"attr is a static method"
attrs[attr_name] = staticmethod(attr)
else:
"attr is a property"
attrs[attr_name] = attr
cls = type(type(obj).__name__,
(cls, type(obj)),
attrs)
return object.__new__(cls)
I tried testing it with:
if __name__ == '__main__':
x=Box(object())
However it comes up with the following error message:
TypeError: __init__() should return None, not 'NotImplementedType'
__init__ is being properly dispatched by isinstance(attr, (types.MethodType, method_wrapper)), and wrapped_attr seems to be executed. Do you have any idea why this is happening?
The problem is here:
for ...:
attr = ...
...
def wrapped_attr(...):
..attr..
This doesn't work as expected, because attr is rebound to various values by the for loop. All subfunctions will see the last value bound, not the value it had in that iteration of the loop. In this case, the last value bound, in alphabetical order, is __subclasshook__, which tends to return NotImplemented when called with random arguments.

Replace property for perfomance gain

Situation
Similar to this question, I want to replace a property. Unlike that question, I do not want to override it in a sub-class. I want to replace it in the init and in the property itself for efficiency, so that it doesn't have to call a function which calculates the value each time the property is called.
I have a class which has a property on it. The constructor may take the value of the property. If it is passed the value, I want to replace the property with the value (not just set the property). This is because the property itself calculates the value, which is an expensive operation. Similarly, I want to replace the property with the value calculated by the property once it has been calculated, so that future calls to the property do not have to re-calculate:
class MyClass(object):
def __init__(self, someVar=None):
if someVar is not None: self.someVar = someVar
#property
def someVar(self):
self.someVar = calc_some_var()
return self.someVar
Problem
The above code does not work because doing self.someVar = does not replace the someVar function. It tries to call the property's setter, which is not defined.
Potential Solution
I know I can achieve the same thing in a slightly different way as follows:
class MyClass(object):
def __init__(self, someVar=None):
self._someVar = someVar
#property
def someVar(self):
if self._someVar is None:
self._someVar = calc_some_var()
return self._someVar
This will be marginally less efficient as it will have to check for None every time the property is called. The application is performance critical, so this may or may not be good enough.
Question
Is there a way to replace a property on an instance of a class? How much more efficient would it be if I was able to do this (i.e. avoiding a None check and a function call)?
What you are looking for is Denis Otkidach's excellent CachedAttribute:
class CachedAttribute(object):
'''Computes attribute value and caches it in the instance.
From the Python Cookbook (Denis Otkidach)
This decorator allows you to create a property which can be computed once and
accessed many times. Sort of like memoization.
'''
def __init__(self, method, name=None):
# record the unbound-method and the name
self.method = method
self.name = name or method.__name__
self.__doc__ = method.__doc__
def __get__(self, inst, cls):
# self: <__main__.cache object at 0xb781340c>
# inst: <__main__.Foo object at 0xb781348c>
# cls: <class '__main__.Foo'>
if inst is None:
# instance attribute accessed on class, return self
# You get here if you write `Foo.bar`
return self
# compute, cache and return the instance's attribute value
result = self.method(inst)
# setattr redefines the instance's attribute so this doesn't get called again
setattr(inst, self.name, result)
return result
It can be used like this:
def demo_cache():
class Foo(object):
#CachedAttribute
def bar(self):
print 'Calculating self.bar'
return 42
foo=Foo()
print(foo.bar)
# Calculating self.bar
# 42
Notice that accessing foo.bar subsequent times does not call the getter function. (Calculating self.bar is not printed.)
print(foo.bar)
# 42
foo.bar=1
print(foo.bar)
# 1
Deleting foo.bar from foo.__dict__ re-exposes the property defined in Foo.
Thus, calling foo.bar again recalculates the value again.
del foo.bar
print(foo.bar)
# Calculating self.bar
# 42
demo_cache()
The decorator was published in the Python Cookbook and can also be found on ActiveState.
This is efficient because although the property exists in the class's __dict__, after computation, an attribute of the same name is created in the instance's __dict__. Python's attribute lookup rules gives precedence to the attribute in the instance's __dict__, so the property in class becomes effectively overridden.
Sure, you can set the attribute in the private dictionary of the class instance, which takes precedence before calling the property function foo (which is in the static dictionary A.__dict__)
class A:
def __init__(self):
self._foo = 5
self.__dict__['foo'] = 10
#property
def foo(self):
return self._foo
assert A().foo == 10
If you want to reset again to work on the property, just del self.__dict__['foo']
class MaskingProperty():
def __init__(self, fget=None, name=None, doc=None):
self.fget = fget
if fget is not None:
self.name = fget.__name__
self.__doc__ = doc or fget.__doc__
def __call__(self, func):
self.fget = func
self.name = func.__name__
if not self.__doc__:
self.__doc__ = func.__doc__
return self
def __get__(self, instance, cls):
if instance is None:
return self
if self.fget is None:
raise AttributeError("seriously confused attribute <%s.%s>" % (cls, self.name))
result = self.fget(instance)
setattr(instance, self.name, result)
return result
This is basically the same as Denis Otkidach's CachedAttribute, but slightly more robust in that it allows either:
#MaskingProperty
def spam(self):
...
or
#MaskingProperty() # notice the parens! ;)
def spam(self):
...
You can change what code a function has by replacing the functions's __code__object with the __code__ object from another function.
Here is a decorator function that I created to do just that for you. Feel free to modify it as you see fit. The big thing to remember though is that the both functions need to have the same number of 'free variables' to be swapped like this. This can easily be done by using nonlocal to force it (as shown below).
NULL = object()
def makeProperty(variable = None, default = NULL, defaultVariable = None):
"""Crates a property using the decorated function as the getter.
The docstring of the decorated function becomes the docstring for the property.
variable (str) - The name of the variable in 'self' to use for the property
- If None: uses the name of 'function' prefixed by an underscore
default (any) - What value to initialize 'variable' in 'self' as if it does not yet exist
- If NULL: Checks for a kwarg in 'function' that matches 'defaultVariable'
defaultVariable (str) - The name of a kwarg in 'function' to use for 'default'
- If None: Uses "default"
Note: this must be a kwarg, not an arg with a default; this means it must appear after *
___________________________________________________________
Example Use:
class Test():
#makeProperty()
def x(self, value, *, default = 0):
'''Lorem ipsum'''
return f"The value is {value}"
test = Test()
print(test.x) #The value is 0
test.x = 1
print(test.x) #The value is 1
Equivalent Use:
#makeProperty(defaultVariable = "someKwarg")
def x(self, value, *, someKwarg = 0):
Equivalent Use:
#makeProperty(default = 0)
def x(self, value):
___________________________________________________________
"""
def decorator(function):
_variable = variable or f"_{function.__name__}"
if (default is not NULL):
_default = default
elif (function.__kwdefaults__ is not None):
_default = function.__kwdefaults__.get(defaultVariable or "default")
else:
_default = None
def fget(self):
nonlocal fget_runOnce, fget, fset, _default #Both functions must have the same number of 'free variables' to replace __code__
return getattr(self, _variable)
def fget_runOnce(self):
if (not hasattr(self, _variable)):
fset(self, _default)
fget_runOnce.__code__ = fget.__code__
return getattr(self, _variable)
def fset(self, value):
setattr(self, _variable, function(self, value))
def fdel(self):
delattr(self, _variable)
return property(fget_runOnce, fset, fdel, function.__doc__)
return decorator

How to make a class property? [duplicate]

This question already has answers here:
Using property() on classmethods
(19 answers)
Closed 3 years ago.
In python I can add a method to a class with the #classmethod decorator. Is there a similar decorator to add a property to a class? I can better show what I'm talking about.
class Example(object):
the_I = 10
def __init__( self ):
self.an_i = 20
#property
def i( self ):
return self.an_i
def inc_i( self ):
self.an_i += 1
# is this even possible?
#classproperty
def I( cls ):
return cls.the_I
#classmethod
def inc_I( cls ):
cls.the_I += 1
e = Example()
assert e.i == 20
e.inc_i()
assert e.i == 21
assert Example.I == 10
Example.inc_I()
assert Example.I == 11
Is the syntax I've used above possible or would it require something more?
The reason I want class properties is so I can lazy load class attributes, which seems reasonable enough.
Here's how I would do this:
class ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
return self.fget.__get__(obj, klass)()
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
type_ = type(obj)
return self.fset.__get__(obj, type_)(value)
def setter(self, func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
self.fset = func
return self
def classproperty(func):
if not isinstance(func, (classmethod, staticmethod)):
func = classmethod(func)
return ClassPropertyDescriptor(func)
class Bar(object):
_bar = 1
#classproperty
def bar(cls):
return cls._bar
#bar.setter
def bar(cls, value):
cls._bar = value
# test instance instantiation
foo = Bar()
assert foo.bar == 1
baz = Bar()
assert baz.bar == 1
# test static variable
baz.bar = 5
assert foo.bar == 5
# test setting variable on the class
Bar.bar = 50
assert baz.bar == 50
assert foo.bar == 50
The setter didn't work at the time we call Bar.bar, because we are calling
TypeOfBar.bar.__set__, which is not Bar.bar.__set__.
Adding a metaclass definition solves this:
class ClassPropertyMetaClass(type):
def __setattr__(self, key, value):
if key in self.__dict__:
obj = self.__dict__.get(key)
if obj and type(obj) is ClassPropertyDescriptor:
return obj.__set__(self, value)
return super(ClassPropertyMetaClass, self).__setattr__(key, value)
# and update class define:
# class Bar(object):
# __metaclass__ = ClassPropertyMetaClass
# _bar = 1
# and update ClassPropertyDescriptor.__set__
# def __set__(self, obj, value):
# if not self.fset:
# raise AttributeError("can't set attribute")
# if inspect.isclass(obj):
# type_ = obj
# obj = None
# else:
# type_ = type(obj)
# return self.fset.__get__(obj, type_)(value)
Now all will be fine.
If you define classproperty as follows, then your example works exactly as you requested.
class classproperty(object):
def __init__(self, f):
self.f = f
def __get__(self, obj, owner):
return self.f(owner)
The caveat is that you can't use this for writable properties. While e.I = 20 will raise an AttributeError, Example.I = 20 will overwrite the property object itself.
[answer written based on python 3.4; the metaclass syntax differs in 2 but I think the technique will still work]
You can do this with a metaclass...mostly. Dappawit's almost works, but I think it has a flaw:
class MetaFoo(type):
#property
def thingy(cls):
return cls._thingy
class Foo(object, metaclass=MetaFoo):
_thingy = 23
This gets you a classproperty on Foo, but there's a problem...
print("Foo.thingy is {}".format(Foo.thingy))
# Foo.thingy is 23
# Yay, the classmethod-property is working as intended!
foo = Foo()
if hasattr(foo, "thingy"):
print("Foo().thingy is {}".format(foo.thingy))
else:
print("Foo instance has no attribute 'thingy'")
# Foo instance has no attribute 'thingy'
# Wha....?
What the hell is going on here? Why can't I reach the class property from an instance?
I was beating my head on this for quite a while before finding what I believe is the answer. Python #properties are a subset of descriptors, and, from the descriptor documentation (emphasis mine):
The default behavior for attribute access is to get, set, or delete the
attribute from an object’s dictionary. For instance, a.x has a lookup chain
starting with a.__dict__['x'], then type(a).__dict__['x'], and continuing
through the base classes of type(a) excluding metaclasses.
So the method resolution order doesn't include our class properties (or anything else defined in the metaclass). It is possible to make a subclass of the built-in property decorator that behaves differently, but (citation needed) I've gotten the impression googling that the developers had a good reason (which I do not understand) for doing it that way.
That doesn't mean we're out of luck; we can access the properties on the class itself just fine...and we can get the class from type(self) within the instance, which we can use to make #property dispatchers:
class Foo(object, metaclass=MetaFoo):
_thingy = 23
#property
def thingy(self):
return type(self).thingy
Now Foo().thingy works as intended for both the class and the instances! It will also continue to do the right thing if a derived class replaces its underlying _thingy (which is the use case that got me on this hunt originally).
This isn't 100% satisfying to me -- having to do setup in both the metaclass and object class feels like it violates the DRY principle. But the latter is just a one-line dispatcher; I'm mostly okay with it existing, and you could probably compact it down to a lambda or something if you really wanted.
If you use Django, it has a built in #classproperty decorator.
from django.utils.decorators import classproperty
For Django 4, use:
from django.utils.functional import classproperty
I think you may be able to do this with the metaclass. Since the metaclass can be like a class for the class (if that makes sense). I know you can assign a __call__() method to the metaclass to override calling the class, MyClass(). I wonder if using the property decorator on the metaclass operates similarly.
Wow, it works:
class MetaClass(type):
def getfoo(self):
return self._foo
foo = property(getfoo)
#property
def bar(self):
return self._bar
class MyClass(object):
__metaclass__ = MetaClass
_foo = 'abc'
_bar = 'def'
print MyClass.foo
print MyClass.bar
Note: This is in Python 2.7. Python 3+ uses a different technique to declare a metaclass. Use: class MyClass(metaclass=MetaClass):, remove __metaclass__, and the rest is the same.
As far as I can tell, there is no way to write a setter for a class property without creating a new metaclass.
I have found that the following method works. Define a metaclass with all of the class properties and setters you want. IE, I wanted a class with a title property with a setter. Here's what I wrote:
class TitleMeta(type):
#property
def title(self):
return getattr(self, '_title', 'Default Title')
#title.setter
def title(self, title):
self._title = title
# Do whatever else you want when the title is set...
Now make the actual class you want as normal, except have it use the metaclass you created above.
# Python 2 style:
class ClassWithTitle(object):
__metaclass__ = TitleMeta
# The rest of your class definition...
# Python 3 style:
class ClassWithTitle(object, metaclass = TitleMeta):
# Your class definition...
It's a bit weird to define this metaclass as we did above if we'll only ever use it on the single class. In that case, if you're using the Python 2 style, you can actually define the metaclass inside the class body. That way it's not defined in the module scope.
def _create_type(meta, name, attrs):
type_name = f'{name}Type'
type_attrs = {}
for k, v in attrs.items():
if type(v) is _ClassPropertyDescriptor:
type_attrs[k] = v
return type(type_name, (meta,), type_attrs)
class ClassPropertyType(type):
def __new__(meta, name, bases, attrs):
Type = _create_type(meta, name, attrs)
cls = super().__new__(meta, name, bases, attrs)
cls.__class__ = Type
return cls
class _ClassPropertyDescriptor(object):
def __init__(self, fget, fset=None):
self.fget = fget
self.fset = fset
def __get__(self, obj, owner):
if self in obj.__dict__.values():
return self.fget(obj)
return self.fget(owner)
def __set__(self, obj, value):
if not self.fset:
raise AttributeError("can't set attribute")
return self.fset(obj, value)
def setter(self, func):
self.fset = func
return self
def classproperty(func):
return _ClassPropertyDescriptor(func)
class Bar(metaclass=ClassPropertyType):
__bar = 1
#classproperty
def bar(cls):
return cls.__bar
#bar.setter
def bar(cls, value):
cls.__bar = value
bar = Bar()
assert Bar.bar==1
Bar.bar=2
assert bar.bar==2
nbar = Bar()
assert nbar.bar==2
I happened to come up with a solution very similar to #Andrew, only DRY
class MetaFoo(type):
def __new__(mc1, name, bases, nmspc):
nmspc.update({'thingy': MetaFoo.thingy})
return super(MetaFoo, mc1).__new__(mc1, name, bases, nmspc)
#property
def thingy(cls):
if not inspect.isclass(cls):
cls = type(cls)
return cls._thingy
#thingy.setter
def thingy(cls, value):
if not inspect.isclass(cls):
cls = type(cls)
cls._thingy = value
class Foo(metaclass=MetaFoo):
_thingy = 23
class Bar(Foo)
_thingy = 12
This has the best of all answers:
The "metaproperty" is added to the class, so that it will still be a property of the instance
Don't need to redefine thingy in any of the classes
The property works as a "class property" in for both instance and class
You have the flexibility to customize how _thingy is inherited
In my case, I actually customized _thingy to be different for every child, without defining it in each class (and without a default value) by:
def __new__(mc1, name, bases, nmspc):
nmspc.update({'thingy': MetaFoo.services, '_thingy': None})
return super(MetaFoo, mc1).__new__(mc1, name, bases, nmspc)
If you only need lazy loading, then you could just have a class initialisation method.
EXAMPLE_SET = False
class Example(object):
#classmethod
def initclass(cls):
global EXAMPLE_SET
if EXAMPLE_SET: return
cls.the_I = 'ok'
EXAMPLE_SET = True
def __init__( self ):
Example.initclass()
self.an_i = 20
try:
print Example.the_I
except AttributeError:
print 'ok class not "loaded"'
foo = Example()
print foo.the_I
print Example.the_I
But the metaclass approach seems cleaner, and with more predictable behavior.
Perhaps what you're looking for is the Singleton design pattern. There's a nice SO QA about implementing shared state in Python.

How to create decorator for lazy initialization of a property

I want to create a decorator that works like a property, only it calls the decorated function only once, and on subsequent calls always return the result of the first call. An example:
def SomeClass(object):
#LazilyInitializedProperty
def foo(self):
print "Now initializing"
return 5
>>> x = SomeClass()
>>> x.foo
Now initializing
5
>>> x.foo
5
My idea was to write a custom decorator for this. So i started, and this is how far I came:
class LazilyInitializedProperty(object):
def __init__(self, function):
self._function = function
def __set__(self, obj, value):
raise AttributeError("This property is read-only")
def __get__(self, obj, type):
# problem: where to store the value once we have calculated it?
As you can see, I do not know where to store the cached value. The simplest solution seems to be to just maintain a dictionary, but I am wondering if there is a more elegant solution for this.
EDIT Sorry for that, I forgot to mention that I want the property to be read-only.
Denis Otkidach's CachedAttribute is a method decorator which makes attributes lazy (computed once, accessible many). To make it also read-only, I added a __set__ method. To retain the ability to recalculate (see below) I added a __delete__ method:
class ReadOnlyCachedAttribute(object):
'''Computes attribute value and caches it in the instance.
Source: Python Cookbook
Author: Denis Otkidach https://stackoverflow.com/users/168352/denis-otkidach
This decorator allows you to create a property which can be computed once and
accessed many times. Sort of like memoization
'''
def __init__(self, method, name=None):
self.method = method
self.name = name or method.__name__
self.__doc__ = method.__doc__
def __get__(self, inst, cls):
if inst is None:
return self
elif self.name in inst.__dict__:
return inst.__dict__[self.name]
else:
result = self.method(inst)
inst.__dict__[self.name]=result
return result
def __set__(self, inst, value):
raise AttributeError("This property is read-only")
def __delete__(self,inst):
del inst.__dict__[self.name]
For example:
if __name__=='__main__':
class Foo(object):
#ReadOnlyCachedAttribute
# #read_only_lazyprop
def bar(self):
print 'Calculating self.bar'
return 42
foo=Foo()
print(foo.bar)
# Calculating self.bar
# 42
print(foo.bar)
# 42
try:
foo.bar=1
except AttributeError as err:
print(err)
# This property is read-only
del(foo.bar)
print(foo.bar)
# Calculating self.bar
# 42
One of the beautiful things about CachedAttribute (and
ReadOnlyCachedAttribute) is that if you del foo.bar, then the next time you
access foo.bar, the value is re-calculated. (This magic is made possible by
the fact that del foo.bar removes 'bar' from foo.__dict__ but the property
bar remains in Foo.__dict__.)
If you don't need or don't want this ability to recalculate,
then the following (based on Mike Boers' lazyprop) is a simpler way to make a read-only lazy property.
def read_only_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)
#_lazyprop.setter
def _lazyprop(self,value):
raise AttributeError("This property is read-only")
return _lazyprop

Categories