I hate getters and setters. They are boring and redundant. The thing I
appreciate most is the const
fields of C++ classes, for those of you who
know how they work. It is something you can't really have in languages
like Java, where final
almost useless and getters are effective only for
base types and immutable objects... But don't get me started on Java, or
this will turn into a rant. Back on topic now!
Quick intro:
First thing first: in Python everything is pretty much public. Even
private instance variables and methods, whose name start with __
, can
be actually accessed if you know how the name mangling works: Basically
the method __foo
of class C
can be accessed from outside C
as
_C__foo
. This is something you should never do, though.
If you want things to be accessible for reading, but you don't want to put
a getter, you can always avoid the __
prefix. That however means
your field can be also written from outside. The outside code might
therefore mess with your internals, possibly violating some invariant or
assumption you do. This is why the private
keyword exists in other
languages!
With resources you can overcome this problem. An example will give you an immediate idea of this:
class C(object):
def __init__(self, x):
self.__foo = x # Private. Well, actually just mangled
@property
def getfoo(self):
return self.__foo
@foo.setter
def setfoo(self, v):
if not good(v):
raise YouSuck('Invalid foo: %r' % v)
self.__foo = v
@foo.deleter
def delfoo(self):
self.__foo = None
c = C(3)
print c.foo # Calls c.getfoo()
c.foo = 100 # Calls c.setfoo(100)
Besides being an elegant syntax, you can use this technique for a number of things:
Check against bad input (as in the example);
Implement abstractions (you think you are accessing a field? Haah! Actually you are triggering an HTTP request in disguise...);
Change code logic without changing the external API (run a method, even if the caller code expects to access a field)
Implement read-only fields (just omit the setter and the deleter, you are done).
Drawbacks? Basically none.
Hacking with syntax
I gave you a quick intro to this simple concept. Now we can hack a bit around.
First I suggest you to have a look to the manual:
pydoc property
As any other decorator, properties can be used without the @
syntax,
just as regular functions.
A mathematician will say they are function having domain in the set of the functions and image in the set of the functions.
A computer scientist will say they are just a high order function.
A software engineer will say they accept functions as input and they return a function.
A hacker will tell you you can spare a bunch of lines of code and/or improve readability/performances/whatever.
A bunch of guys I know will say something about black boxes, and mess up things with blind copy-and-paste examples from StackOverflow.
Here I put some ways of using them. Good or bad? It is up to you.
Technique one: backed object.
I often use object orientation as a form of currification, in order to carry around some context. Example:
class C(object):
def __init__(self):
self.resource = ...
def get_something(self):
return D(self)
class D(object):
def __init__(self, c):
self.__c = c
def do_something(self, y):
# Using the resource of self.__c
self.__c.resource.use(self.__x, y)
c = C()
d = c.get_something()
d.do_something(1) # No need to pass c.resource around
d.do_something(2) # No need to pass c.resource around
d.do_something(3) # No need to pass c.resource around
By using properties
we can improve this a little:
class C(object):
def __init__(self):
self.resource = ...
@property
def something(self):
return D(self)
class D(object):
def __init__(self, c):
self.__c = c
def do_something(self, y):
self.__c.resource.use(y)
c = C()
d = c.something # As it was a field
d.do_something(1)
d.do_something(2)
d.do_something(3)
Or have a lazy-evaluated field, backed by the object and instantiated whenever needed:
class C(object):
def __init__(self):
self.resource = ...
self.__d = None
@property
def something(self):
if self.__d is None:
self.__d = D(self)
return self.__d
class D(object):
def __init__(self, c):
self.__c = c
def do_something(self, y):
self.__c.resource.use(self.__x, y)
c = C()
c.something.do_something(1) # Instantiated and used
c.something.do_something(2) # Re-used
c.something.do_something(3) # Re-used again
Non-decorated uses
Need an instance of D
which is backed by an instance of C
? You do not
really need to have a getter...
class D(object):
def __init__(self, x):
self.x = x
class C(object):
d = property(D)
c = C()
assert c.d.x is c
This is because:
@property
ofdef d(self): ...
is equivalent tod = property(d)
, namely the output ofproperty
replaces the original methodd
;Calling
c.foo()
is equivalent to callC.foo(c)
Calling c.d()
as is like calling D(c)
and returning its result. The
field x
of the instance of D
will be initialized with a reference
to c
.
Write-only fields:
Take another glance at the pydoc property
, then look at this:
class E(object):
lol = property(fset=lambda s, v : s.__setter(v))
def __setter(self, v):
print 'Hello', v
# Here possibly check, assign, whatever...
e = E()
e.lol = 3 # Prints "Hello 3"
#print e.lol # FAILS. No getter!
The property
function accepts three optional functions as actual
parameter: the getter
, the setter
and the deleter
respectively. Each
of them takes the instance as parameter. The setter also takes a value
to be set. We can actually replace it with a lambda expression.
Interestingly, the name mangling works transparently, since we are within the class definition.
I think that's enough for today. Let's take a nap.