Couldn't quite get a straight answer from other threads on this one:
In Python, what's the main difference between using
class Foo(object):
def __init__(self, x):
self.x = x
and
class Foo(object):
def __init__(self, x):
self._x = x
@property
def x(self):
return self._x
By the looks of it using @property in this way makes x read-only.. but maybe someone has a better answer? Thanks /Fred
For your example, yes this allows you to have read-only attributes. Further, properties can allow you to seem to have more attributes than you actually do. Consider a circle with .radius
and .area
. The area can be calculated based on the radius, rather than having to store both
import math
class Circle(object):
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return math.pi * (self.radius ** 2)
The property
decorator implementation uses the descriptor protocol, which is how we do data encapsulation in Python OOP. A descriptor is:
In general, a descriptor is an object attribute with “binding behavior”, one whose attribute access has been overridden by methods in the descriptor protocol. Those methods are
__get__()
,__set__()
, and__delete__()
. If any of those methods are defined for an object, it is said to be a descriptor.
Typically, in other OOP languages you use getters and setters. You'll frequently see people coming from, say, Java writing a Python class like this:
class A(object):
def __init__(self, x, y):
self._x = x
self._y = y
def getX(self):
return self._x
def getY(self):
return self._y
def setX(self, x):
self._x = x
def setY(self, y):
self._y = y
def some_method_that_uses_attributes(self):
return self.getX() + self.getY()
This is very much not how you would do things in Python. The point of getters and setters is to provide data encapsulation. We encapsulate access to a data attribute by wrapping that in a getter and setter. Then, if we ever want to add something, say, make sure x
is never set to a value below 10 (as a contrived example), we simply change the way setX
is implemented, and we don't have to modify the rest of our code. In Python, however, we would write the above class as follows:
class A(object):
def __init__(self, x, y):
self.x = x
self.y = y
def some_method_that_uses_attributes(self):
return self.x + self.y
A person coming from Java might recoil in horror: "You aren't properly encapsulating your class! This will become a maintenance nightmare!"
Nope, because we have descriptors/properties:
class A(object):
def __init__(self, x, y):
self._x = x
self.y = y
@property
def x(self):
return self._x
@x.setter
def x(self, val):
if val > 10:
self._x = val
else:
raise ValueError("x must be greater than 10")
def some_method_that_uses_attributes(self):
return self.x + self.y
And now, we don't have to refractor every method that uses self.x
, like some_method_that_uses_attributes
. And more importantly, clients of our class don't have a broken interface when we make this change! This is nice because it lets us avoid writing a bunch of boilerplate code, and implementing descriptors is relatively straightfoward in the cases where we do need it. Also, this makes our code nice and pretty, without self.get_this()
and self.set_that(3)
all over the code, and the much more readable self.this
and self.that = 3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With