Reputation: 710
I have a class that stores some attributes. These attributes are numpy arrays with some floats inside. I want this attributes to be accessed when creating objects. What I don't want is them to be modified if any operation is done to a external variable that holds the value of the attribute.
This is simple to do with getters/setters or properties with other types of variables, but it seems to fail with numpy arrays.
I have wrote a simple script that tests every kind of possible solution I know. It works for integer attributes but fails with numpy arrays.
This is the test class:
class test_class:
# Initialization
def __init__(self, attribute1, attribute2, attribute3):
self.attribute1 = attribute1
self._attribute2 = attribute2
self._attribute3 = attribute3
# Attribute 1 with getter and setter
def get_attr1(self):
return(self.attribute1)
def set_attr1(self, value):
self.attribute1 = value
# Attribute 2 as a property with getter and setter
def get_attr2(self):
return(self._attribute2)
def set_attr2(self, value):
self._attribute2 = value
attribute2 = property(get_attr2, set_attr2)
# Attribute 3 as a property
@property
def attribute3(self):
return(self._attribute3)
@attribute3.setter
def attribute3(self, value):
self._attribute3 = value
Testting it with integers as attributes:
test = test_class(10, 100, 1000)
print test.get_attr1()
print test.attribute2
print test.attribute3
a1 = test.get_attr1()
a2 = test.attribute2
a3 = test.attribute3
a1 += 5
a2 += 50
a3 += 500
print test.get_attr1()
print test.attribute2
print test.attribute3
Output as expected, without the attributes being modified externally:
10
100
1000
10
100
1000
Testing it with numpy arrays:
import numpy as np
test = test_class(np.array([10,20,30]), np.array([100,200,300]), np.array([1000,2000,3000]))
print test.get_attr1()
print test.attribute2
print test.attribute3
a1 = test.get_attr1()
a2 = test.attribute2
a3 = test.attribute3
a1 += 5
a2 += 50
a3 += 500
print test.get_attr1()
print test.attribute2
print test.attribute3
Output not as expected, the values have been changed:
[10 20 30]
[100 200 300]
[1000 2000 3000]
[15 25 35]
[150 250 350]
[1500 2500 3500]
So if not getters/setters nor properties work with numpy arrays, what can be done?
EDIT:
Well, I found a solution to this problem using the copy.deepcopy
function. Now it works as expected.
Attribute definition:
from copy import deepcopy
class test_class:
...
# Attribute 4 with getter and setter using deepcopy
def get_attr4(self):
return(deepcopy(self.attribute4))
def set_attr4(self, value):
self.attribute4 = value
Testing:
test = test_class(np.array([10,20,30]), np.array([100,200,300]), np.array([1000,2000,3000]), np.array([10000,20000,30000]))
...
print test.get_attr4()
...
a4 = test.get_attr4()
...
a4 += 5000
...
print test.get_attr4()
Result:
...
[10000 20000 30000]
...
[10000 20000 30000]
Upvotes: 0
Views: 2420
Reputation: 85522
NumPy arrays are mutable, integers are not.
>>> a = 1
>>> id(1)
4297261152
>>> a += 1
>>> id(a)
4297261184
Note: The id
changes.
As opposed to:
>>> arr = np.arange(5)
>>> d(arr)
4331954736
>>> arr += 10
>>> id(arr)
4331954736
>>> arr
array([10, 11, 12, 13, 14])
Note: The id
stays the same.
It does not matter that you use a = test.get_attr1()
or a = test.attribute2
. The a
you get is a Python object, as pretty much everything is an object you deal with in Python. Once you have a
it does not matter how you created it either by assignment a = 1
or as return value from a method a = test.get_attr1()
(The property just makes for a nicer syntax for a method call.). Then a
is just a name for an object and you work with this object. If it is mutable like NumPy arrays the +=
typically changes the values inside. For immutable this simply not possible.
If you do not want to modify these objects, you can male a copy. Usually with the help of the module copy
. NumPy arrays provide their own copy method:
>>> arr2 = arr.copy()
>>> arr
array([10, 11, 12, 13, 14])
>>> arr2 += 100
>>> arr2
array([110, 111, 112, 113, 114])
>>> arr
array([10, 11, 12, 13, 14])
Upvotes: 3