Skip to content
TURNKEYGNU / LINUX
AppsDocumentationBlogScreenshotsGitHubTurnKey Hub
Home/Blog/Pythonic attribute magic (property, customized attribute access)

From the project

Pythonic attribute magic (property, customized attribute access)

By Liraz Siri3 November 20143 min read
developmentpythonpythonic

Many languages encourage programmers to use a getter/setter pattern.

Like this:

instance.set_foo(value) print instance.get_foo()

Many years ago, as a junior programmer still drinking the C++ enforced protection kool-aid, I believed this was a good idea. I even used this pattern for data attributes I needed to protect from the hypothetical rogue programmer who would mess around with my classes insides in a way I hadn't intended. Or something like that. Sure, using getters and setters was tedious, verbose, and seemed to be in conflict with the spirit of the Don't Repeat Yourself rule, but surely that was a small price to pay on my way down the Righteous Path of object oriented programming.

Thankfully, like many other programmers in search of improved productivity I eventually moved away from bondage and discipline type programming languages (for most things) and towards high level interpreted languages like Python with a more relaxed attitude. Under this influence many bad OO habits were miraculously cured, including all uses of the getter/setter pattern that didn't involve dynamic code invocation (e.g., get/set data from a database).

Then one day I realized that Python had some magic that could free me forever from the getter/setter pattern. Instead I would just do the simplest possible thing:

instance.foo = value print instance.foo

And behind the scenes the Python magic could implement what looks like a routine attribute access using code of arbitrary complexity that would, for example, serialize value into a file on set, and read it back out from the file and unserialize it on get.

Here is a bit of example code that shows three techniques that can be used to implement Pythonic attributes:

# High-level properties are functional in nature and are easiest to
# use and understand.

class HighLevelProperty(object):
    def __init__(self):
        self._var = None

    def get_var(self):
        print "get_var() => %s" % `self._var`
        return self._var

    def set_var(self, val):
        print "set_var(%s)" % `val`
        self._var = val

    var = property(get_var, set_var)

# Low level property classes give you more control and power in
# exchange for greater verbosity.

class LowLevelProperty(object):
    class Descriptor(object):
        def __get__(self, obj, cls):
            print "__get__(%s, %s) => %s" % (`obj`, `cls`, `obj._var`)
            return obj._var

        def __set__(self, obj, val):
            print "__set__(%s, %s)" % (`obj`, `val`)
            obj._var = val

    def __init__(self):
        self._var = None

    var = Descriptor()

# Customized attribute access is most appropriate when you want
# systematic control over a classes attributes.

class CustomizedAttributeAccess(object):
    # class-level attributes are defined before customized
    # attribute access, which operate at the instance level

    _var = None

    def staticattribute(self):
        print "staticattribute() doesn't go through __getattr__"

    def __getattr__(self, name):
        print "__getattr__(%s)" % `name`
        if name == 'var':
            return object.__getattribute__(self, '_var')

        raise AttributeError("only 'var' is supported")

    def __setattr__(self, name, val):
        print "__setattr__(%s, %s)" % (`name`, `val`)
        if name == 'var':
            object.__setattr__(self, '_var', val)

        raise AttributeError("only 'var' is supported")

I've ordered them by ease of use, but you'll need to use these techniques a bit to get a feel for which is most appropriate under what circumstances.

A good real-world example of Pythonic attribute access is in TKLBAM registry.py module, where I high level functional properties to implement a simple data store object with attributes transparently serialized/unserialized from the filesystem.

Keep exploring

Find your next server

Browse ready-to-use appliances by the job you need to do.

Explore the app library
TURNKEYGNU / LINUX

Own your infrastructure without starting from scratch.

Explore TurnKey
AboutDocumentationFAQBlogGitHub
Free and open sourceSecure by defaultReady to deploy