Why `self.__setattr__(attr, value)` trigger `__getattribute__`?

In the following example,when the code run to the line self.__setattr__(attr, value),the self.__setattr__ in the line invoke __getattribute__,maybe python interpreter just read part of code:

import time
class Room:
    def __init__(self,name):
        self.name = name
    def __getattribute__(self,attr):
        print('in __getattribute__',attr)
        return  object.__getattribute__(self,attr)
    def __setattr__(self,attr,value):
        print('in __setattr__',attr)
        time.sleep(3)
        self.__setattr__(attr, value)

Initialize the class Room:

r = Room('r1')
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name
in __getattribute__ __setattr__
in __setattr__ name

You can see that self.__setattr__(attr, value) trigger __getattribute__,why python interpreter do not read out the whole line self.__setattr__(attr, value),then do the assignment with trigger nothing?

When the code run to the self.name = name,python interprter read the whole line self.name = name,parse it as an assignment,parse it as an assignment,why not extract self.name and jump into __getattribute__ as it deal with self.__setattr__(attr, value)?

Python has to find the __setattr__ method itself before it can call it. (You then have infinite recursion in your __setattr__ method, which is why it then loops.)

Despite being a “special method”, __setattr__ is still simply a method call. But you CAN bypass the checks in the Room class itself, because - since a method call is just a function call anyway - you can call the method from object instead:

object.__setattr__(self, attr, value)

Note that, since self is the instance but object is the type, you now need to explicitly pass self as the first parameter. You only need to make this change inside def __setattr__(...) itself; everything else is fine. That will prevent the infinite recursion.

1 Like

Python has to find the __setattr__ method itself before it can call it.But python jump into __getattribute__.
You only solve the issue,but not give an explanation!WHAT i WANT is why,what you give is how to solve.
When the code run to the self.name = name,python interpreter read the whole line self.name = name,parse it as an assignment,why not extract self.name and jump into __getattribute__ as it deal with self.__setattr__(attr, value)?

1 Like
   def __getattribute__(self,attr):
       print('in __getattribute__',attr)
       return  object.__getattribute__(self,attr)
   def __setattr__(self,attr,value):
       print('in __setattr__',attr)
       time.sleep(3)
       self.__setattr__(attr, value)

The __getattribute__ method is called before any other attribute
method. (It is more usual to define __getattr__ if you need this, which
is called only for missing attributes.) So __getattribute__ is called
to look up __setattr__ because that is its job.

[… example run …]
You can see that self.__setattr__(attr, value) trigger
__getattribute__,why python interpreter do not read out the whole
line self.__setattr__(attr, value),then do the assignment with
trigger nothing?

Because that’s not what Python does. This line:

 self.__setattr__(attr, value)

is evaluated in several pieces:

  • self: Python locates this name in the active namespaces, and finds
    it in the method namespace, defined by the method parameters
  • .__setattr__: Python now looks up the attribute __setattr__ on the
    object it found from self. Because self is an instance of Room
    and Room has a __getattribute__ method, that method is called
    first. This is when you see the print...in __getattribute__.
    This evaluates to a “bound method”. It hasn’t called it yet.
  • (attr, value): this (coming directly after the preceeding
    expression) is a function call parameter list. Python looks up the
    names attr and value to get their objects, and then calls the
    bound method from the previous step with those parameters.
    This is when you see the print...in __setattr__.

You might be thinking that Python should see self.__setattr__(...) at
compile time and know directly to call Room.__setattr__, but it does
not. The name self is not special, it is just a convention, and might
be any object. So all the normal “find this object’s method”
procedures are then followed as described above.

Cheers,
Cameron Simpson cs@cskk.id.au

Not sure what you mean by “jump into” here. The way to find __setattr__ is to call __getattribute__.

When the code run to the self.name = name(in the __init__),python interpreter read the whole line self.name = name,parse it as an assignment,why not extract self.name and call __getattribute__ as it deal with self.__setattr__(attr, value)?

Ah, that’s because that’s an assignment target. You don’t care what was previously in self.name, you only want to give it a new value. If you were doing something like self.items[2] = name, that would be looking up self.items and assigning inside it, but the assignment to the attribute doesn’t.

import time
class Room:
    def __init__(self,name):
        self.name = name
    def __getattribute__(self,attr):
        print('in __getattribute__',attr)
        return  object.__getattribute__(self,attr)
    def __setattr__(self,attr,value):
        print('in __setattr__',attr)
        time.sleep(3)
        self.__dict__[attr] = value

What happen?No endless loop!

r = Room('r1')
in __setattr__ name
in __getattribute__ __dict__

With same logic as self.__setattr__(attr, value),there would be an endless loop!

So, here’s another assignment statement, like the one Chris pointed to.
We’re not looking up the current value of self.name, we’re
setting it. So __getattribute__ is not called to find name here.

The __getattr__ and __getattribute__ methods are for finding the
current value of an attribute.

However, to set the value Python does call the Room class
__setattr__ method. It doesn’t need __getattribute__ to find it
because Python defines __setattr__ to do that.

Frankly, this was a mild surprise to me because I hadn’t thought about
this as deeply as you are trying to do. But if it wasn’t this way it
probably wouldn’t be possible to give a class a __getattribute__
method at all (consider: how would Python itself find the
__getattribute__ method itself?)

Then…

But inside your __setattr__ you set an entry in self.__dict__. And
to evaluate self.__dict__ we do call __getattribute__ to look up
__dict__, because self.__dict__ is a Python expression you have
written which has to be evaluated from scratch.

Because self.__setattr__ means “the attribute named __setattr__ of the object named self”. That has to be found, before it can be called.

The base object.__getattribute__ explains the complete process for finding an attribute, so __getattribute__ will be called (by a special way implemented inside the interpreter, so it won’t recurse itself infinitely) on every attribute access. A subclass can override it to change the logic completely. This is a powerful and hard-to-use tool. Most of the time we should use __getattr__ instead. The basic __getattribute__ logic will try the “normal” ways first (looking at the actual structure of the object, then of its class, etc.), and then also try __getattr__ only if nothing was found the normal way. __getattr__ for the attributes of an object, is similar to __missing__ for keys in a dict subclass.

Python does not “interpret” code this way. It first compiles into a bytecode, and then the bytecode is interpreted. Each piece of the bytecode does something smaller. For example, self.name = name will turn into opcodes to put name and self onto the stack, then an opcode to set the attribute. When the interpreter handles these opcodes, it can look up special methods like __setattr__, __getattribute__ etc. magically.

But the difference is in the actual structure of the code. Nothing in this code tries to read an existing attribute value, so there is no reason that __getattribute__ will be used at runtime. Because of the structure of the code, the compiler already knows to use the __setattr__ magic - in its special way, not the Python code way.

But self.__setattr__(attr, value) means to read the __setattr__ attribute of self (which could be a bound method that comes from the class), and then call whatever is found when we do that. The compiler does not have any special treatment for names like __setattr__, or self. It just sees a . that has self on one side, and __setattr__ on the other.

A post was split to a new topic: How does getattribute with estate work?