0

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]

1 Answer 1

3

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]) 
Sign up to request clarification or add additional context in comments.

4 Comments

I knew there was a difference in the object type, in this case mutable vs immutable. In my edit you can see that I used the copy library to provide a deep copy of the numpy array in the getter method, so that a new vector is returned. This way I can make operations with the array without modifying the attribute itself. Thanks anyway for the tip, it was really valuable.
To be honest, your answer was valuable to understand the differences between these types of objects. However it didn't solve my actual problem. I think that someone with my problem will find more interesting my edit than your explanation. Anyway, I accepted yours.
@IñigoHernáezCorres Added information about copying. Thanks for the hint.
Nice to know there is a copy method for numpy arrays. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.