Reputation: 105
I'm having trouble on how to implement property to protect attributes.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def set_x(self, x):
if '_x' in dir(self):
raise NotImplementedError("Cannot change x coordinate")
else:
self._x = x
def get_x(self):
return self._x
#I beleive my mistake is here. I'm not sure if I'm implementing this correctly
x = property(get_x, set_x, None, None)
So I want to prevent any user from changing the x-coordinate. My question is, how do I get python to redirect the user to the set_x() and get_x() methods? I've tried running this code in terminal and whenever I apply the following, the point changes.
p = point(3, 4)
p.x = 5 #x is now 5
Upvotes: 1
Views: 156
Reputation: 59096
You only need this much:
class Point:
def __init__(self, x, y):
self._x = x
self.y = y
def get_x(self):
return self._x
x = property(get_x)
You can set the hidden field self._x
in your init
, then you don't need a setter for x
at all. And have get_x
return self._x
rather than self.x
so it doesn't try and call itself.
You can use the @property
decorator to do this even more succinctly.
class Point:
def __init__(self, x, y):
self._x = x
self.y = y
@property
def x(self):
return self._x
Upvotes: 5
Reputation: 309821
The following code works on both python2.x and python3.x:
class Point(object):
def __init__(self, x, y):
self.x = x
self.y = y
def set_x(self, x):
if '_x' in dir(self):
raise NotImplementedError("Cannot change x coordinate")
else:
self._x = x
def get_x(self):
return self._x
x = property(get_x, set_x, None, None)
p = Point(2, 3)
print(p.x) # 2
p.x = 6 # NotImplementedError
Pretty much all I did was inherit from object
(to get it to work on python2.x) and use the name Point
rather than point
(which would have been a NameError
before).
There are other things you can do to clean it up a bit (e.g. khelwood's suggestion of just writing the getter -- or DSM's suggestion of using hasattr
instead of '_x' in dir(self)
).
Note, if you really just want a type that takes an x
and y
arguments that you want to be immutable -- Maybe you should consider using a colledctions.namedtuple
from collections import namedtuple
Point = namedtuple('Point', 'x,y')
p = Point(2, 3)
p.x # 2
p.y # 3
p.x = 6 # AttributeError: can't set attribute
Upvotes: 4