You are here
Pythonic attribute magic (property, customized attribute access)
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.
Add new comment