问题
In Python, we can use the @property decorator to manage access to attributes. For example, if we define the class:
class C:
def __init__(self,value):
self._x = value
@property
def x(self):
"""I'm the 'x' property."""
return self._x
we can get the value of x, but not change it:
c = C(1)
#c.x = 4 # <= this would raise an AttributeError: can't set attribute
However, if the attribute is of a mutable type (e.g., a list), we can set a different value for a position of the attribute:
c = C([0,0])
c.x[0] = 1 # <= this works
Is there a way to prevent it? If x is a list, I would like to able to change the value of positions of x only using methods of class C.
回答1:
One way to do this would be to return a copy of the attribute, rather than the list itself.
>>> class C:
... def __init__(self, value):
... self._x = value
... @property
... def x(self):
... return self._x[:]
...
>>> c = C([1, 2, 3])
>>> c.x
[1, 2, 3]
>>> c.x.append(5)
>>> c.x
[1, 2, 3]
>>> c.x[0] = 6
>>> c.x
[1, 2, 3]
Alternatively, the property could return an iterator over attribute, or a view (for example dict.items() instead of a dict). Returning iterators or views may help limit memory use if the attribute is large, and is more consistent with the behaviour of modern Python builtin functions and types.
If the mutable attribute contains mutable attributes itself - for example a list of lists or dictionaries - then it may be necessary to return copies these objects too. This can be expensive in terms of time and resources if the object graph is deep. See the docs for the copy module for ways to customise how objects are copied.
This technique is commonly used to prevent the problem of aliasing - where other objects hold references to your object's internal state.
It does mean that the copies may go out of sync with the real attribute, but if your code is well designed then other classes should not be holding onto the values of your class anyway.
来源:https://stackoverflow.com/questions/51114984/how-to-manage-access-to-a-mutable-attribute-in-python