Naeem Ahmed
Naeem Ahmed

Reputation: 105

How to use property()

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

Answers (2)

khelwood
khelwood

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

mgilson
mgilson

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

Related Questions