2

I created a Pixel class for image processing (and learn how to build a class). A full image is then a 2D numpy.array of Pixel but when I added a __getattr__ method , it stopped to work, because numpy wants an __array_struct__ attribute.

I tried to add this in __getattr__:

if name == '__array_struct__':
    return object.__array_struct__

Now it works but I get

'''DeprecationWarning: An exception was ignored while fetching the attribute __array__ from an object of type 'Pixel'. With the exception of AttributeError NumPy will always raise this exception in the future. Raise this deprecation warning to see the original exception. (Warning added NumPy 1.21)

I = np.array([Pixel()],dtype = Pixel)'''

a part of the class:

class Pixel:
    def __init__(self,*args):

        #things to dertermine RGB
        self.R,self.G,self.B = RGB
        
        #R,G,B are float between 0 and 255
    ...
    def __getattr__(self,name):
 
        if name == '__array_struct__':
            return object.__array_struct__
        if name[0] in 'iI':
            inted = True
            name = name[1:]
        else:
            inted = False
 
        if len(name)==1:
            n = name[0]

            if n in 'rgba':
                value = min(1,self.__getattribute__(n.upper())/255)
       
            elif n in 'RGBA':
                value = min(255,self.__getattribute__(n))
                assert 0<=value
            else:
                h,s,v = rgb_hsv(self.rgb)
                if n in 'h':
                    value = h
                elif n == 's':
                    value = s
                elif n == 'v':
                    value = v
                elif n == 'S':
                    value = s*100
                elif n == 'V':
                    value = v*100
                elif n == 'H':
                    value = int(h)
            if inted:
                return int(value)
            else:
                return value
        else:
            value = []
            for n in name:
                try:
                    v = self.__getattribute__(n)
                except AttributeError:
                    v = self.__getattr__(n)
                if inted:
                    value.append(int(v))
                else:
                    value.append(v)
            return value
tdelaney
  • 73,364
  • 6
  • 83
  • 116
Gnathon
  • 23
  • 6
  • 2
    What is the question? :) - It may be a good idea to put up a part or all of the class so that me can see what you are trying to achieve. – Diblo Dk Feb 05 '23 at 18:21
  • The question is, how do i remove these Warnings? – Gnathon Feb 05 '23 at 18:41
  • If it is only a matter of not showing the warning this link should help: https://stackoverflow.com/a/879249/678611 These notifications are part of the way that developers tell users what will be removed in a new major release, e.g. Python 4. You should therefore look at doing it in a different way, not because it matters right now, but it will at some point. – Diblo Dk Feb 05 '23 at 19:06
  • Thank you, I'll do this for the moment, but since i don't understand why `__getattr__` has this effect, i cant fix it – Gnathon Feb 05 '23 at 19:17
  • You can't fix it, it's not meant to be. You must therefore find out what they will do in the future and use that method of progress. - I may need to mention that it may also be that the method will be removed in the next major release of numpy or Pixel. But Python, numpy, Pixel will probably be compatible for a long time from now, as they must be backwards compatible. However, you should always move forward. – Diblo Dk Feb 05 '23 at 19:23
  • I did not see that. It is NumPy that comes up with the warning - `Warning added NumPy 1.21` – Diblo Dk Feb 05 '23 at 19:30

2 Answers2

1

When you are in a class method and want a fallback, fall back to the same method on the super() proxy:

import numpy as np


class Pixel:
    def __init__(self, value):
        self.value = value

    def __getattr__(self, name):
        print(f"{name!r} was requested for {self}")
        return super().__getattr__(name)


I = np.array([Pixel(1), Pixel(2)], dtype=Pixel)
'__array_struct__' was requested for <__main__.Pixel object at 0x7f6ccc40e970>
'__array_interface__' was requested for <__main__.Pixel object at 0x7f6ccc40e970>
'__array__' was requested for <__main__.Pixel object at 0x7f6ccc40e970>
'__array_struct__' was requested for <__main__.Pixel object at 0x7f6ccc40e910>
'__array_interface__' was requested for <__main__.Pixel object at 0x7f6ccc40e910>
'__array__' was requested for <__main__.Pixel object at 0x7f6ccc40e910>
paime
  • 2,901
  • 1
  • 6
  • 17
  • I added it but i still get DeprecationWarnings – Gnathon Feb 05 '23 at 18:59
  • The attribute doesn't exist on `object` so it doesn't really matter whether you do `object.whatever` or `super().whatever`. It raises the same `AttributeError`. But that's not where your bug is. Something other than `AttributeError` is raised later on in the method. – tdelaney Feb 05 '23 at 19:27
  • @tdelaney you're right, just wanted to note that it's best practice to delegate to `super()`, and especially to delegate to the **same method**: in that case then no need to know that the correct value to return was `AttributeError`. – paime Feb 06 '23 at 12:13
1

Your class should either implement __array__ or raise an AttributeError when numpy tries to get it. The warning message says you raised some other error and that numpy will not accept that in the future. I haven't figured out your code well enough to know, but it could be that calling self.__getattr__(n) inside of __getattr__ hits a maximum recursion error.

object.__array_struct__ doesn't exist and so just by luck its AttributeError exception is what numpy was looking for. A better strategy is to raise AttributeError for anything that doesn't meet the selection criteria for your automatically generated attributes. Then you can take out the special case for __array_struct__ that doesn't work properly anyway.

tdelaney
  • 73,364
  • 6
  • 83
  • 116
  • __getattr__ was raising a TypeError somewhere (a mistake), now that it's fixed , everything works, thank you ! – Gnathon Feb 05 '23 at 19:32