Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

numpy array subclass unexpedly shares attributes across instances

Tags:

python

numpy

I am having a weird subclass numpy.ndarray issue that feels like
Values of instance variables of superclass persist across instances of subclass
But I have not been able to understand fully or make it work for for my example.

Reading through Slightly more realistic example - attribute added to existing array I am trying to do pretty much exactly this. I want to add an attrs attribute to an array to hold information such as units in a dictionary.

Here is what I have:

import numpy
class dmarray(numpy.ndarray):
    def __new__(cls, input_array, attrs={}):
        obj = numpy.asarray(input_array).view(cls)
        obj.attrs = attrs
        return obj

    def __array_finalize__(self, obj):
        # see InfoArray.__array_finalize__ for comments
        if obj is None:
            return
        self.attrs = getattr(obj, 'attrs', {})

So then to use it and demonstrate the issue

a = dmarray([1,2,3,4])
b = dmarray([1,2,3,4])
a.attrs['foo'] = 'bar'
print(b.attrs)
#{'foo': 'bar'}
b.attrs is a.attrs
# True  # hmm....

So b is picking up attrs that I don't want it to. Annoyingly it works fine if you do this:

from datamodel import *
a = dmarray([1,2,3,4], attrs={'foo':'bar'})
b = dmarray([1,2,3,4])
b.attrs
# {}

So how in the world do I make this dmarray work how I want it to?


Edit: OK so this seems to fix the problem but I don't understand why. So lets change the question to what this is doing and why it works?

class dmarray(numpy.ndarray):
    def __new__(cls, input_array, attrs=None):
        obj = numpy.asarray(input_array).view(cls)
        return obj

    def __init__(self, input_array, attrs=None):
        if attrs == None:
            attrs = {}
        self.attrs = attrs

So by removing the kwarg from __new__() and putting it in __init__() it works. I just tried this as a "well it might work"

a = dmarray([1,2,3,4])
b = dmarray([1,2,3,4])
a.attrs['foo'] = 'bar'
b.attrs
# {}
like image 553
Brian Larsen Avatar asked Apr 07 '11 21:04

Brian Larsen


1 Answers

The problem is here:

def __new__(cls, input_array, attrs={})

Never do this attrs={} in a function header. The expected result is (probably) not what you think it is. This is a common Python Pitfall. See here Default Parameter Values in Python

The right way how to do this:

def __new__(cls, input_array, attrs=None):
    if attrs is None:
        attrs = {}
like image 172
Fenikso Avatar answered Sep 18 '22 17:09

Fenikso