2

I am trying to understand the function of the dunder getattribute and getattr methods. While experimenting, I noticed an unexpected shape attribute showing up in my class. I can't find any explanation for why this is happening.

class X:
    def __init__(self, count):
        self.count = count

x = X(42)

results in x showing in PyCharm debug mode as:

x = {X}
  count = {int}42

whereas

class X:
    def __init__(self, count):
        self.count = count

    def __getattribute__(self, item):
        # Calling the super class to avoid recursion
        return super(X, self).__getattribute__(item)

    def __getattr__(self, item):
        return self.__setattr__(item, 'fred')

x = X(42)

results in x showing in PyCharm debug mode as:

x = {X}
  count = {int}42
  shape = {str} 'fred'

Where did the attribute "shape" come from and what is its purpose?

swright573
  • 41
  • 2
  • `__getattr__` is used when the normal attribute lookup fails. PyCharm appears to be trying to access `shape` (for whatever reason), and `x` responds by *creating* that attribute with the value `"fred"`. `__getattribute__`, on the other hand, *always* gets called first. `object.__getattribute__` is basically what decides that `__getattr__` should be used when normal lookup via `__dict__` fails. – chepner Jul 09 '21 at 14:47
  • You *rarely* want to override `__getattribute__`. – chepner Jul 09 '21 at 14:47

1 Answers1

0

The simple answer is that x.__getattr__ creates the shape attribute should anyone try to access it. Because there is no existing shape attribute on either x or X, x.shape is resolved by calling x.__getattr__('shape').

I have no explanation for who (PyCharm itself?) tries to access shape or why they would do so.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thanks for the help. I just tried this in IDLE and shape doesn't show up so it is a PyCharm thing. That hadn't occurred to me. – swright573 Jul 09 '21 at 14:57
  • 1
    PyCharm seems to using the presence or absence of a `shape` attribute to determine how to treat the object (maybe as a test for whether it is `numpy.array`-like). The issue here is that you have made the unorthodox decision to autovivify (to borrow a Perl term) the attribute when it does not exist. – chepner Jul 09 '21 at 15:02
  • `__getattr__` should *probably* not mutate the object. Usually you want to return a default value or derive a value from some other attributes, but not actually create any attributes or change any existing attribute values in the process. – chepner Jul 09 '21 at 15:03
  • I just tried this in IDLE using the version of my class referencing __getattribute__ and __getattr__. When I referred to x.shape a second time, I saw it had been set to 'fred" by __getattr__. Perhaps there's something going on in PyCharm along the same lines. – swright573 Jul 09 '21 at 15:14
  • 1
    It turns out this is a known bug in PyCharm (https://youtrack.jetbrains.com/issue/PY-48306) – swright573 Jul 13 '21 at 11:22