February 06, 2009 at 19:11 Tags Python

I don't like getters and setters. Besides being too wordly and often unnecessary, getters and setters make APIs less clean. Consider:

print dog.get_name() dog.set_name( 'bozo' )

Versus:

print dog.name dog.name = 'bozo'

Isn't the second one so much nicer and cleaner? No big difference? Then try to think of a complex class with dozens of attributes, is it fun writing brain-dead getters and setters for each?

Getters and setters belong to the sad world of Java and C++. This is because in those languages you have no choice. Suppose you just make a member public and let the class's clients access it. What happens when you need to change the behavior of the member, or perhaps make it virtual (i.e. compute it on the fly from other members), or make something complex happen when it's assigned? You then turn the member into a getter/setter pair and have a whole lot of code to rewrite. This is why in C++/Java, people use getters and setters from the start.

But in languages like Python it's unnecessary! You can just begin with a plain attribute, letting clients access it. If, at some point, a need will arise to hide the attribute from direct access, no problem, just use the property function.

The canonical example is degrees and radians. Suppose you've been only using radians in your code, so Vector.angle was a simple attribute. But for some reason, you had to switch to degrees for the internal representation. What to do about the KLOCs of user code expecting angle to be just an attribute returning the angle in radians? No problem:

import math class Vector ( object ): def __init__ ( self , angle_rad): self .set_angle_rad(angle_rad) def get_angle_rad ( self ): return math.radians( self ._angle_deg) def set_angle_rad ( self , angle_rad): self ._angle_deg = math.degrees(angle_rad) angle = property (get_angle_rad, set_angle_rad) def get_angle_deg ( self ): return self ._angle_deg def set_angle_deg ( self , angle_deg): self ._angle_deg = angle_deg angle_deg = property (get_angle_deg, set_angle_deg) v = Vector( 2 *math.pi) print v.angle print v.angle_deg v.angle = math.pi print v.angle print v.angle_deg

See how we've easily turned the angle attribute into a virtual entity accessible via getter and setter functions.

The point is, just use attributes, not getters and setters. If you ever need to change the code for some reason (this will happen much less often than you expect), you can always change the internals but still present the old API to client code.