Encapsulation Methods In Python
Chris Curvey February 06, 2009
|
There's a lot of ways to deal with encapsulation in Python, and
you'll probably see a bunch of them if you debug other people's
code. Here's a survey of the methods that you can use and some
suggestions on when and how to use them.
|
The Simplest Case
This is probably similar to the first class you ever wrote:
class Company:
def __init__(self, revenue, cost):
self.revenue = revenue
self.cost = cost
self.profit = revenue - cost
firstCompany = Company(100, 90)
assert firstCompany.profit == 10
But it has an obvious problem. There's no way to stop another
program from setting the calculated "profit" variable directly:
enron = Company(100, 90)
enron.profit = "10 Meeeeelion Dollars!"
The Underscore Convention
By tradition in the Python world, member variables that start
with an underscore are not supposed to be modified, except with
the class or it's subclasses (in other words, it's OK to modify
_foo if you're modifying self._foo. It is frowned upon to
modify bar._foo.) So you could do something like this:
class Company2:
def __init__(self, revenue, cost):
self.revenue = revenue
self.cost = cost
self._profit = revenue - cost
But how do we get the value of "profit"? In other languages,
you might do something like this:
def getProfit(self):
return self._profit
c2 = Company2(110, 80)
assert c2.getProfit() == 30
__getattr__
There are two problems with this solution. First, a lazy
programmer can still do "assert c2._profit == 30" and it will
work just fine. The second problem is that "c2.profit" feels so
natural, so why not keep that style? Before Python 2.0, there
was a "special" method called __getattr__ that you could use to
keep the accessor style the same, and yet still have the
"profit" value calculated for you:
# Pre-Python 2
class PrePython2Company:
def __init__(self, revenue, cost):
self.revenue = revenue
self.cost = cost
self._profit = revenue - cost
def __getattr__(self, name):
if name == "profit":
return self._profit
else:
raise AttributeError()
pp2c = PrePython2Company(120, 70)
assert pp2c.profit == 50
assert pp2c.revenue == 120
You probably won't use __getattr__ anymore, as Python 2.0 came
up with a better way: Properties...
Encapsulation Methods In Python
Properties
|