This is the second article following up Phillip J. Eby’s Python Is Not Java. In the first article, The Static Method Thing, we took a look at how Java static methods differ from Python class/static methods. This time we’re going to dive deep into the evils of getters and setters.
I’m hopping around a bit because this was the item that I saw the most confusion around in comments and trackback posts. I’m also a bit worried that some walked away with the impression that getters and setters are always evil and should never be used, ever, in any language, ever. Getters and setters are not GOTOs, and Phillip’s original post never came close to making that claim.
Let’s take a look at what Phillip had to say about getters and setter use in Python:
Getters and setters are evil. Evil, evil, I say! Python objects are not Java beans. Do not write getters and setters. This is what the ‘property’ built-in is for. And do not take that to mean that you should write getters and setters, and then wrap them in ‘property’. That means that until you prove that you need anything more than a simple attribute access, don’t write getters and setters. They are a waste of CPU time, but more important, they are a waste of programmer time. Not just for the people writing the code and tests, but for the people who have to read and understand them as well.
In Java, you have to use getters and setters because using public fields gives you no opportunity to go back and change your mind later to using getters and setters. So in Java, you might as well get the chore out of the way up front. In Python, this is silly, because you can start with a normal attribute and change your mind at any time, without affecting any clients of the class. So, don’t write getters and setters.
I thought he explained this well. There’s not a lot missing here so I’m going to dive real deep and try to give a lot of background and code examples on the situation.
From the top
To start from the beginning, let’s take a look at what getters and setters look like in Java… Actually, no. Let’s back up even further and follow the trail to where the first realization of needing getters and setters might take place.
Here’s a nice simple class, Contact, with member variables for storing the
kinds of information you might find in an Address Book application:
public class Contact {
public String firstName;
public String lastName;
public String displayName;
public String email;
public C() {}
public C(firstName, lastName, displayName, email){
this.firstName = firstName;
this.lastName = lastName;
this.displayName = displayName;
this.email = email;
}
public void printInfo() {
System.out.println(this.displayName +
" <" + this.email + ">");
}
}
We release version 1.0 of our Contact Management API and everyone rushes to use the wealth of functionality it provides in their own address book applications. Now there’s code everywhere that looks like this:
Contact contact = new Contact();
contact.firstName = "Phillip";
contact.lastName = "Eby";
contact.displayName = "Phillip J. Eby";
contact.email = "x@x.com";
contact.printInfo();
Later, the feature requests begin pouring in from excited developers around the world:
- Automatically capitalize
firstNameandlastNamewhen set. - Calculate
displayNamefromfirstNameandlastNameif one isn’t set explicitly. - Validate that the email address set has an “@” in it.
Now we’re screwed. We need a mechanism for attaching behavior to our member variables but that mechanism does not exist in Java. It was thus that the getter/setter convention was spat out onto an otherwise pure Java community.
The Getter/Setter Convention
The getter/setter pattern
says that public member variables should be
used only when you are absolutely positive that you will never need to attach
behavior to member variable access. If you need behavior attached to your
member variables, or you believe you will some time in the future, you should
use the getter/setter convention when the class is initially designed:
Make the member variable non-public. If you want to hide the member variable from subclasses and classes in the same package, forcing everyone to go through the get/set methods, use
private. If you believe having direct access to the member variable from subclasses is good, useprotected.Write
getandsetmethods for the member variable.
Let’s take a look at what our Contact class would have looked like if it had
been designed properly by using the getter/setter convention:
public class Contact {
private String firstName;
private String lastName;
private String displayName;
private String email;
public C(){}
public C(firstName, lastName, displayName, email){
setFirstName(firstName);
setLastName(lastName);
setDisplayName(displayName);
setEmail(email);
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String value) {
firstName = value;
}
public String getLastName() {
return lastName;
}
public void setLastName(String value) {
lastName = value;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String value) {
displayName = value;
}
public String getEmail() {
return email;
}
public void setEmail(String value) {
email = value;
}
public void printInfo() {
System.out.println(this.getDisplayName() +
" <" + this.getEmail() + ">");
}
}
The ability to now go back and add code to the get/set methods is the reason we use the getter/setter convention. If we had some mechanism for adding code later, we could just use public member variables.
The Brush and The Canvas
There are a few things worth calling out right now as this is where Python programmers reading this are going to start feeling a bit queasy:
- The number of lines have increased by almost 400%.
- The immediate functionality gain for those extra lines is zero.
- The getter/setter methods follow an obvious and boring pattern (perfect for a machine to deal with).
Here’s where I see a huge difference in mind-set between Python and Java coders… Practices like getter/setter that lead to code bloat are generally met with less resistance in the Java community because the intelligent IDEs and other tools go a long way in managing the excess code:
Who cares if the source file is 4000 lines, I have a nice tree-view over
here showing me the ten method signatures and whatnot.
In Java, the tool (IDE) is the brush and the code is the canvas. In Python, the language is the brush and the machine is the canvas – the language and the tool are the same thing. In Java you enhance the IDE, in Python you enhance the language.
This is the primary reason why there is little serious demand for Python IDEs. Many people coming to Python can’t believe no one uses IDEs. The automatic assumption is that Python is for old grey beards who are comfortable with vi and Emacs and refuse to accept breakthroughs in programming productivity like IDEs. Then they write a little Python code and realize that an IDE would just get in their way.
Getter/Setter Convention in Python
Minor syntax differences aside, Python has the same getter/setter convention as
Java. The difference is when it is applied. Let’s take a look at the Python
implementation of the basic Contact class:
class Contact(object):
def __init__(self, first_name=None, last_name=None,
display_name=None, email=None):
self.first_name = first_name
self.last_name = last_name
self.display_name = display_name
self.email = email
def print_info(self):
print self.display_name, "<" + self.email + ">"
And code to use it:
contact = Contact()
contact.first_name = "Phillip"
contact.last_name = "Eby"
contact.display_name = "Phillip J. Eby"
contact.email = "x@x.com"
contact.print_info()
Pretty much the same as the Java example before adding get/set methods.
Now we need to implement some logic that will raise an exception if an email address is set that doesn’t look like an email address:
class Contact(object):
def __init__(self, first_name=None, last_name=None,
display_name=None, email=None):
self.first_name = first_name
self.last_name = last_name
self.display_name = display_name
self.email = email
def print_info(self):
print self.display_name, "<" + self.email + ">"
def set_email(self, value):
if '@' not in value:
raise Exception("This doesn't look like an email address.")
self._email = value
def get_email(self):
return self._email
email = property(get_email, set_email)
What’s happened here is that we were able to add get/set methods and still maintain backward compatibility. The following code still runs properly:
contact = Contact()
contact.email = "x@x.com"
When the email attribute is set, the set_email method is called. When
the email attribute is got
, the get_email method is called.
The basic value to take away from all this is that you want to strive to make sure every single line of code has some value or meaning to the programmer. Programming languages are for humans, not machines. If you have code that looks like it doesn’t do anything useful, is hard to read, or seems tedious, then chances are good that Python has some language feature that will let you remove it.
What not to do
This article would not be complete without giving an example of what not to do in Python:
class Contact(object):
def __init__(self, first_name=None, last_name=None,
display_name=None, email=None):
self.set_first_name(first_name)
self.set_last_name(last_name)
self.set_display_name(display_name)
self.set_email(email)
def set_first_name(self, value):
self._first_name = value
def get_first_name(self):
return self._first_name
def set_last_name(self, value):
self._last_name = value
def get_last_name(self):
return self._last_name
def set_display_name(self, value):
self._display_name = value
def get_display_name(self):
return self._display_name
def set_email(self, value):
self._email = value
def get_email(self):
return self._email
def print_info(self):
print self.display_name, "<" + self.email + ">"
Last Notes
Many of the other items Phillip mentioned are truly just differences in idiom. I hope my Python bias didn’t effect the quality of this article. My goal has been to attack the issues Phillip raised with as little bias as possible but this is an item where I believe Python has an approach that is obviously superior.
Comments
Very insightful!
The “Enhance the IDE vs. enhance the language” concept gave me a new way to think about Python and explain it to others.
Thank you.
— Kari Hardarson on Sunday, November 18, 2007 at 09:05 PM #
ok, this makes sense and I must say that I’m guilty of using a lot of getter/setters. I often run into a problem where I have 10 properties that need to register the value (setter) in more than on place. But other than the property name they are alike.
So, in that case what is the answer? A loop of dynamically created property/methods?
— ben on Friday, December 14, 2007 at 07:33 AM #
Hi, on thi
__init__it should be:Thnak you very much for such a great text. Began here http://tomayko.com/articles/2004/12/15/the-static-method-thing and hoping to read a lot more.
— Rafael Zanella on Tuesday, January 29, 2008 at 12:12 AM #
oops, sorry for the typos.
— Rafael Zanella on Tuesday, January 29, 2008 at 12:14 AM #
No Typos there. ‘Thnak’ is the Sound of an Hand, clapping respectful on a shoulder. Thnak, Ryan! (;
— Nils-Hero on Monday, February 18, 2008 at 11:03 PM #
It’s not that simple. In this particular case by converting an attribute to a setter you are making arguably a worse sin: you are changing the, uhh, I’m not even sure how to call it, the attribute’s contract (contracts are normally defined for classes or methods) without changing the name. A setattr may now raise an exception! For starters, I don’t like that on aesthetic grounds: foo = ‘bar’ is OK, but o.foo = ‘bar’ blows up; OK, that’s not a serious argument ;) Problem is, even if you control all accessing code, you could miss some instances which did not expect the exception. If you don’t control the accessors, you’ve just delivered to your client a nice little timebomb. I much prefer for such things to blow up in my face immediately and get quickly fixed rather than manifest in production.
So I’m not a big lover of setters ;) I do use them occassionally: mostly for transparent caching and programmer checks (meant to make sure my objects are consistent, to catch programmer errors rather than user errors). I try to avoid them whenever I run into anything slightly complex. Complexity is to be expected for method calls, not for measly random setattrs.
— Gintas on Thursday, March 20, 2008 at 11:45 AM #
Good article. One nit-picky thing, though.
class Contact(object):def __init__(self, first_name=None, last_name=None,display_name=None, email=None):...self.email = emaildef set_email(self, value):...raise Exception('blah')email = property(get_email, set_email)Doing —–>
x = Contact()breaks, because set_email gets called with None in the init(), which blows up.So at the very least, one would have to call
Contact()with a valid email address.Sure, I’m nitpicking example code, but it actually points out a design feature.
You would want either the member attribute or the property to be called something either than ‘email’, or your code will blow up.
— Anonymous Coward on Monday, March 24, 2008 at 09:36 AM #
Gah, I wish there was a preview feature. I’m not an anonymous coward, I’m Dave. The previous 2 posts were mine :P
— Dave on Monday, March 24, 2008 at 09:38 AM #
You could summarize this entire article by simply stating ‘A feature of Python is that you can basically override the assignment operator for class-level variables to call a validating function’ or something in that direction. Which, by the way, is pretty neat and would be convenient in Java. Not that I’m actually bothered by accessors, since they’re auto-generated by my IDE (yes, ide) and they’re easy to ignore.
As for an IDE, I haven’t worked with Python yet, but I’d seriously like an IDE to point out typos to me with neat red underlines and for class- and method documentation to appear when I’m looking for a particular method (or to be more precise, looking for a particular bit of functionality). By not using an IDE, or at least as it seems in my point of view, you’re forced to only find errors when you run the program, and forced to go to the internets or dive into an external code file to read documentation. Using an IDE is, for those two tasks, certainly a big advantage.
— Cthulhu on Tuesday, July 15, 2008 at 01:06 AM #
@Cthulhu: VIM has syntax highlighting, which sort of works similarly. I know when learning Perl a week ago, I’d immediately notice if something I just typed didn’t “light up” with the correct highlighting, so I assume something similar would happen with Python (which I have yet to learn (shame on me)).
StumbleUpon’d
— Izkata on Friday, July 25, 2008 at 05:05 PM #
There was a long discussion in Python mailing list about accessors vs public attributes (http://mail.python.org/pipermail/python-list/2006-July/394282.html). While “Getters and setters are evil. Evil, evil, I say!” sounds very dramatic, a mature person should think for herself/himself rather than getting into a religious group claiming everything outside it to be evil.
I know of at least one python library that uses a lot of getters and setters: PyQt. Why don’t they allow access to the fields in a “pythonic” way rather than providing “ugly” accessors? I realized after creating a Python module myself. The reason is as follows: Suppose you have a class called MyClass with a public attribute name. class MyClass: def init(self):
def printName(self):
So, in Python, one can write perfectly legal code like:
Of course the output is not what the user wanted because the attribute was capitalized in assignment and Python silently took it as a new attribute of the object. While this may be a trivial case, for more complex classes the user is likely to introduce these hard to find bugs. This can be avoided if the attributes are exposed using accessors. Because, calling a non-existent method will throw an error. This is especially applicable to C/C++ extension modules as there is no real benefit of giving public access to attributes ( they will go through function calls anyways ).
— rays on Wednesday, September 03, 2008 at 07:06 PM #
I’m a Python newbie and I found your post useful.
However, I’m a big fan of making immutable objects – all variables are set in the constructor, and then they have getters, but not setters. Typically I wouldn’t even use a getXXX type of method name – as the “property” is immutable I’d call the getter just XXX.
I’d like to be able to do this without actually writing any getters as such, and I suppose in Java I can by using public final variables (although years of hearing about how important getters and setters are has conditioned me to reject this idea)… I’m hoping there’s a good way to do something similar in Python somehow, because I like the idea of not writing getters and setters, but I do want to be able to use read-only properties.
— Bromley on Tuesday, November 25, 2008 at 04:04 AM #
i read somewhere that you can also use this “feature” to implement some kind of class security and make properties read-only or private based on the caller.
— Anonymous Coward on Thursday, January 15, 2009 at 05:02 AM #
It seems that the preferred way to do getters/setters now is with decorators. In Guido’s words…
— Barron on Friday, January 16, 2009 at 02:53 PM #
re. IDE’s : thank you Cthulhu!
Plus, running this all from the command line makes it seem so much harder to ‘touch’…
Thanks!
— Dan on Saturday, May 09, 2009 at 09:52 PM #
I hate public attributes on general principles (breaks encapsulation) so I’m not impressed with the rant on evil. Neither am I enthused by the property decorator described in http://docs.python.org/library/functions.html (someone above suggested decorators).
What I am looking for is a shortcut to creating default getter/setter’s until I get around to implementing the extra logic I know I’ll need later. I suppose I’ll just abuse my IDE.
BTW: it staggers me that anyone would think you don’t need an IDE for ANY language no matter how awesome. The best I’ve found thus far is a combination of Eclipse, the pyDev Eclipse plugin, and pyLint (which integrates seamlessly into Eclipse via pyDev). Good news: it’s free and wicked useful. Bad news: setting it up for the first time will probably set you back an hour or two.
— Denis on Thursday, July 02, 2009 at 12:12 PM #
Uh, maybe this is just me, but all you have done is write getters and setters and wrapped them in a call to property(). Which is just exactly what you said not to do.
Maybe I’m missing some tiny detail as to why this is not the case, but to my eyes, the only difference between Java and Python here is simply indentation.
If I really have to write a getter/setter for every member variable in a class, then properties are a waste of space and (more importantly) my time. Now, if I can use lambdas, then everything’s cool.
Someone let me know why I’m wrong.
— RT Butte on Friday, July 31, 2009 at 12:05 AM #
@RT Butte: the idea is that you don’t have to write a bunch of getters and setters for all of your properties from the start. Instead, you add them when you need them, and only for the properties you actually need them for.
— DNS on Friday, July 31, 2009 at 12:21 PM #
I totally agree with Gintas. The thing is this: when you give the impression that something is writable from outside people will use it that way. Going in later to protect that member from garbage may help your specific object, but will most definitely not protect the application.
At the expense of a lot of critique: To me, getters and setters are nothing more then making a field public without getting your ass kicked by purists. Sure, you have control of what goes in and out… but as long as the application doesn’t know that something might not get in or out, it will still break.
— Anonymous Coward on Friday, August 21, 2009 at 04:22 AM #