2
\$\begingroup\$

I recently asked a question on StackOverflow, looking for a way to more easily turn a class attribute into a @property, but only if no other value has been provided to the class constructor. A simple way to do this is

class Demo(object):
    def __init__(self, value=None):
        self._value = value

    @property
    def value(self):
        if self._value is None:
            # some calculations to get value
            self._value = value
        return self._value

which I considered quite some overhead, so my question there asked for a decorator implementing this, and I came up with this solution

import functools

# https://stackoverflow.com/a/28961925/321973
def property_if_None_generator(f, cached=False):
    @functools.wraps(f)
    def wrapped(self):
        value = self.__getattribute__('_' + f.__name__)
        if value is not None:
            return value
        else:
            return f(self)
    if cached:
        wrapped = functools.lru_cache()(wrapped)
    return property(wrapped)

property_if_None = lambda f: property_if_None_generator(f, cached=False)
cached_property_if_None = lambda f: property_if_None_generator(f, cached=True)     

then the question's example would turn into

class Demo(object):
    def __init__(self, value=None):
        self._value = value

    @chached_property_if_None
    def value(self):
        # some calculations to get value
        return value

Now I would appreciate any feedback on both the decorator itself and whether this is actually acceptable as opposed to the non-decorator version mentioned above. I'm especially concerned about the question whether it is a good idea to implicitly use the underscore-prepended attribute/property-name.

I have also considered making things even shorter by using another decorator (or even a metaclass) such that the __init__ wouldn't even have to do the self._value = value assignment, I'd also appreciate opinions on that.

edit Since the SO-answer merely received a downvote without comment so far, I'd really appreciate feedback on this...

\$\endgroup\$
  • \$\begingroup\$ Just to clarify — you wish to only support read-only properties? \$\endgroup\$ – 200_success Mar 16 '15 at 10:14
  • \$\begingroup\$ If this is readonly, why wouldn't you handle the None case in (or from) the initialiser? Then the property becomes trivial. \$\endgroup\$ – jonrsharpe Mar 16 '15 at 10:39
  • \$\begingroup\$ @200_success Good point, yes, it's read-only, and... \$\endgroup\$ – Tobias Kienzler Mar 16 '15 at 10:51
  • \$\begingroup\$ ...@jonrsharpe it's a lengthy calculation if no value is provided (I only stated that on SO, sorry), so it'd be best in a separate method anyway "- and if it's already a method, why not make it a property directly?" was my thought then \$\endgroup\$ – Tobias Kienzler Mar 16 '15 at 10:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.