Attribute Fetch for Built-ins Skips Instances [Solved]

Hello aficionados,

I am chugging along and currently on the topic of Attribute Fetch for Built-ins Skips Instances of my Pythonic studies.

I am testing the following test script:

class C:                 # New-style: 3.X and 2.X

    data = 'spam'

    def __init__(self, one, two):

        self.one = one
        self.two = two
      
    # Test 1: Comment out --- Test 2: Uncomment out
    def __getattribute__(self, item):
        print('item = ', item)
        return '<getattribute>: ' + item

    def __getitem__(self, i):    # Redefine built-ins
        print('<getitem>: ' + str(i))
        return self.data[i]      # Run expr or getattr


X = C(33, 55) # Create instance of object
print(X[1])  # __getitem__ called for index operation

# Returned for __getattribute__ intercept call
# <getattribute>: data 

From the print statement, you can see that we are performing an indexing operation. The index operation causes the __getitem__ overloading intercept method to be automatically called. There are two test cases here.

  1. __getattribute__ intercept method is commented out
  2. __getattribute__ intercept method is not commented out.

Test observations in order of tests enumerated above:

  1. The index passed in is 1 (one). The __getitem__ overloading method is called due to the indexing operation. It has the return expression self.data[i]. This calls the class attribute data to be called. The letter returned is a function of the index i, passed in. So far so good.

  2. This one is a bit trickier. The reason being is that

`data` = ' <getattribute>:  ' + item.

Or, data = item according to the print statement.

But data[i] equals the string character of the following total string:

 '<getattribute>: data'.

You can change the value of the index passed in, in the print statement to verify.

Can someone please offer an explanation to help in my understanding as to why item equals data and data[i] equals a string element of (depending on the index passed in):

<getattribute>: data

What is self.data ? When you intercept all attribute lookups, self.data is whatever’s returned from type(self).__getattribute__(self, 'data') - not something from the dictionary. If you don’t want that, you’ll need to either (a) special-case it inside getattribute, or (b) use __getattr__ instead, which is only called for missing attributes.

2 Likes

Thank you for responding to my query. This concept is a bit subtle. Let me ponder this a bit more.

Ok, I looked at this a little longer.

    def __getattribute__(self, item):

        return '<getattribute>: ' + item

    def __getitem__(self, i):

        return self.data[i]  # Explicit attribute name call
                             # initiates __getattribute__ method call

The return statement in the __getitem__ method initiates a __getattribute__ intercept method call because of the self.data explicit name attribute fetch. This sends the data attribute name as a string argument to the __getattribute__ intercept method. Its return statement is now:

`<getattribute>: data`  # string

Got it up to here so far.

How does the data attribute both become an argument and the total return value of which it is an element of? As noted in the original post, data[i] indexes the entire __getattribute__ return string highlighted above. Just need this small detail to make it crystal.

UPDATE:
I think that I understand it now. The return value from the __getattribute__ intercept method (the string composed of <getattribute>: data ) is the fetch value for the variable data[:]. Thus, an index will represent a character from this string.

Yep! Nailed it!