[Tutor] Get/Set/Attribute Accessors in Python?
Steven D'Aprano
steve at pearwood.info
Wed Dec 5 17:58:03 CET 2012
On 06/12/12 02:38, Malcolm Newsome wrote:
> Hey Tutors,
>
> Python is/was my first language. Yet, I've recently begun learning C# for
> my new job.
>
> One thing I've come across in C# (and, quite frankly, am having a difficult
> time grasping) is Get and Set (Accessors).
>
> Since, I don't ever recall reading about this in Python, I'm wondering if
> they exist. If not, what is the "thinking" behind why they are not
> included in the language?
They exist, if you program them. Here's a class with getter and setter accessors:
class Example(object):
def __init__(self, value):
self.setx(value)
def getx(self):
return self.x
def setx(self, value)
self.x = value
instance = Example(42)
print instance.getx() # gives 42
instance.setx(23)
And here is how it should be written in Python:
class Example(object):
def __init__(self, value):
self.x = value
instance = Example(42)
print instance.x # gives 42
instance.setx(23)
Accessors are methods that you must call to get or set a data value. Some
languages (C# and Java, for example) strongly encourage you to use accessors
instead of exposing the data value directly. Why? "Just in case."
Notice how trivially simple my accessors were? Well, they're not always that
trivial. Often you want the getter to calculate a value, or the setter to
enforce some restriction. Here's a toy example:
class Example(object):
def __init__(self, value):
self.setx(value)
def getx(self):
print "I'm the x value!"
return self.x
def setx(self, value)
if value < 0:
raise ValueError('x must not be negative')
self.x = value
If you just exposed self.x as Python does, you can't perform any calculations
unless you use accessor methods. So Java and C# have the philosophy:
- Some day you might need to do a calculation when getting or setting x.
- But if you expose data attribute x directly to the caller, you are committed
to *always* exposing x to the caller.
- So you better not expose x directly, you should always use accessor methods,
just in case some day you need them.
But with Python, you never need accessor methods, because we can wrap attribute
access with methods behind the scene! This work is done using the "property"
function. You start by trivially exposing data attribute x directly to the caller:
class Example(object):
def __init__(self, value):
self.x = value
then later on, if you need to turn x into a computed attribute, you
simply use property to wrap some accessor methods with the same name
as the data attribute:
class Example(object):
def __init__(self, value):
self.x = value
@property
def x(self):
print "I'm the x value!"
return self._x # see below
@x.setter
def x(self, value):
if value < 0:
raise ValueError('x must not be negative')
self._x = value
As far as the caller is concerned, there is no change, she still
writes the same direct attribute code:
instance = Example(42)
print instance.x
instance.x = 23
as before.
Did you notice the mysterious reference to self._x above? That leads
neatly to your next question.
> Additionally, a separate, but perhaps related question is that I have not
> seen public/private classes in Python. How might this factor into the
> whole accessor scenario? (Or, am I trying to relate two topics that have
> nothing to do with each other?)
Private versus Public (of classes, methods, attributes, whatever) has to
do with information hiding.
Information hiding is a very useful idea in programming. In a nutshell,
you might have a class that promises to provide certain features, the
interface, but everything else is an implementation detail that might
go away if you find a better way to program the class.
For example, everything inside a function -- the local variables -- are
utterly private. The caller cannot peek inside a method and grab the
local variables, they can only see the return result:
def hundred():
x = 25 # this is local to the function and invisible from outside
return x + x + x + x # but this is exposed publicly
If I now improve this function:
def hundred():
return 100
from the outside you have no way of knowing that local variable x has
disappeared. This is a Good Thing.
This philosophy of hiding implementation details is extended to classes,
methods, and attributes. Hence, languages like C#, C++, Java and others
distinguish between Public and Private attributes. Public attributes are
visible to the caller, private ones are not.
The problem is, like all good things, it is possible to have too much
data hiding. People go mad and make everything private, forcing the caller
to reinvent the wheel. Consequently you will often find programmers
plaintively asking "I need to do foo, and this class does exactly what I
want, but the foo() method is private, how do I get access to it?"
For most languages, it is possible to to defeat the compiler and get access
to supposedly private attributes. It's just messy and complicated, but
people can and do. (Even local variables are not *truly* hidden. If a
debugger can peek inside a function, so can you, if you do what the debugger
does.)
So Python takes a different approach. The Python philosophy is:
"We're all adults here. If you want to shoot yourself in the foot, we
won't stop you."
Python doesn't enforce hiding of public and private attributes. If it
did, people would find ways to break it, so instead we have a simple
convention for public and private.
Anything starting with a single underscore, like self._x, is considered
to be private. If you mess with it, you have no-one to blame but yourself
if the code shoots you in the foot. You have been warned.
Anything not starting with a single underscore is public.
(Names with double leading and trailing underscores, like __init__, are
reserved for Python's use as special methods or attributes. You can use
them if Python defines them, but don't use them for your own stuff.)
--
Steven
More information about the Tutor
mailing list