2

To give you some context, yesterday I came across this post. I found the problem quite interesting, so I tried to find a solution that would keep the syntax as close as possible to what was asked. Here is what I came up with:

class DummyCube:
    cubes = []

    @classmethod
    def __getattribute__(cls, name):
        print('is this called?')
        attributes = [getattr(cube, name) for cube in cls.cubes]

        return attributes


class Cube:
    def __init__(self, volume):
        DummyCube.cubes.append(self)
        self.volume = volume


a = Cube(1)
b = Cube(2)
c = Cube(3)

print(DummyCube.__getattribute__('volume'))
print(DummyCube.volume)

I think my approach is way too complicated (or just generally not very good), especially compared to the currently accepted answer, but fortunately, that's not really relevant to what I would like to know.

My approach fails, because DummyCube.volume raises an AttributeError: type object 'DummyCube' has no attribute 'volume', which is of course true, but I don't understand why my custom __getattribute__ class method is not called. Invoking the method directly works. Is it not possible to do this at all? What am I missing here? As far as I understand, __getattribute__ is what is called first, when using the '.'-notation, but the error traceback does not let me confirm this.

mapf
  • 1,906
  • 1
  • 14
  • 40
  • 2
    You need to put the method on the *meta*class, not just make it a class method, see e.g. https://stackoverflow.com/questions/15214386/is-there-a-method-like-getattribute-for-class-not-instance-variables – jonrsharpe May 27 '20 at 08:57
  • 1
    I see. I'm not really sure I understand why though. Are you saying that the `super().__getattribute__` method is invoked before my own implementation when calling it from a class level? – mapf May 27 '20 at 09:02
  • 1
    ``super()`` delegates to the base class (next class in the MRO, actually). The meta class is *not* the base class. The relation is ``issubclass(some_class, base_class)`` but ``isinstance(some_class, meta_class)``. In your case, ``DummyCube``'s base class is ``object`` and its meta class is ``type``. – MisterMiyagi Jun 03 '20 at 20:26

2 Answers2

0

No idea if this solves your problem. But to call your getattribute you need to include parentheses.

class Cube:
    def __init__(self, volume):
        DummyCube().cubes.append(self)
        self.volume = volume
Yehla
  • 199
  • 11
  • The parentheses instantiate the `DummyCube` class, but I would like to avoid that since I'm interested in class methods. Nevertheless an interesting suggestion! – mapf Jan 18 '21 at 13:46
0

I see. I'm still learning Python, so my suggestions might not be that great. Don't worry this will be the last one ^^. I just noticed that this might be what you are looking for.

class DummyCube:
    cubes = []

    @classmethod
    def __getattribute__(cls, name):
        print('is this called?')
        attributes = [getattr(cube, name) for cube in cls.cubes]
        return attributes

class Cube:
    def __init__(self, volume):
        self.volume = volume
        DummyCube.cubes.append(self)
 
a = Cube(1)
b = Cube(2)
c = Cube(3)

print(DummyCube.__getattribute__('volume'))
print(DummyCube.cubes[0].volume)
print(DummyCube.cubes[1].volume)
print(DummyCube.cubes[2].volume)

Btw. I still don't get how this works :)

Yehla
  • 199
  • 11
  • The reason why my approach doesn't work is because `DummyCube.volume` doesn't call `__getattribute__()`. Your case works because you're accessing the `DummyCube.cubes` attribute, which was never the issue. But thanks for giving it a try! – mapf Jan 19 '21 at 16:42