Read-only attributes via properties versus documentation

In Python I am writing a class where some attributes must remain constant during the lifetime of the object.

One way is to use properties:

class Foo:
    def __init__(self, x, y):
        '''Create a Foo object

        x -- list of int (READ-ONLY)
        y -- float
        '''
        # Save x and y and ensure they are int
        self._x = [int(xx) for xx in x]
        self.y = float(y)

    @property
    def x(self):
        # Return a copy of self._x to prevent accidental modification
        return self._x.copy()

      

Is this good practice? Should I rely only on the documentation to show which attributes are writable and which are not? Any other suggestion?

+3


source to share


4 answers


First, note that there is practically no technical way to prevent anyone from accessing your object's implementation and modify it if they really want to.

Now the answer / your question / example:



  • using the implementation attribute ( _x

    ) and the readonly property makes the intent perfectly clear.

  • if your list really needs to be immutable, use tuple()

    . It's semantically controversial ( tuple

    intended for position-based records - counts relational db records, etc.), not an immutable list), but make sure it will be immutable and avoid having to make copies.

  • you still want to clearly document this attribute, which must "remain constant" for the lifetime of the instance.

+1


source


You can change your code a little to get at least a complaint

class Foo:
    def __init__(self, x, y):
        '''Create a Foo object

        x -- list of int (READ-ONLY)
        y -- float
        '''
        # Save x and y and ensure they are int
        self._x = [int(xx) for xx in x]
        self.y = float(y)

    @property
    def x(self): return self._x

    @x.setter
    def x(self,value):
        raise AttributeError('Don\'t touch my private parts, please')

      

And then if you try to change the situation:



>>>> a = Foo([1,2,3,4,5],0.2)

>>>> a.x
     [1, 2, 3, 4, 5]

>>>> a.x = 3
Traceback (most recent call last):

  File "<ipython-input-87-0a024de0ab56>", line 1, in <module>
    a.x = 3

  File "D:/pydump/gcd_lcm_tests.py", line 230, in x
    raise AttributeError('Don\'t touch my private parts, please')

AttributeError: Don't touch my private parts, please

      

Instead of raising an exception, you can feel free to pass

, but in my opinion it is better to complain.

+1


source


I believe you after this:

Example # 1:

from collections import namedtuple

foo = namedtuple("Immutable", ["a", "b"])

      

Example # 2:

class Foo(object):
    __slots__ = ()

      

In both cases, it is an error to set attributes or change objects in any way.

Demo (s):

>>> from collections import namedtuple
>>> class Foo(object):
...     __slots__ = ()
... 
>>> Bar = namedtuple("Bar", ["a", "b"])
>>> foo = Foo()
>>> bar = Bar(1, 2)
>>> foo.a = 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'a'
>>> foo.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'a'
>>> bar.a
1
>>> bar.a = 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't set attribute

      

Note that with an object, namedtuple

you cannot change any of its attributes after they are created. It is very convenient.

0


source


If you want the attribute not to change, don't return a copy on dereferencing: you'll just confuse things further and lead to strange errors. If you want to enforce immutability, turn it into a property and make the setter refuse to modify it.

How well this works depends on the details of your data structure. For example, if your attribute is a list, you can intercept the modification of the list itself, but if it has mutable elements, you can still change their attributes.

0


source







All Articles