• getting rid of the recursion in __getattribute__

    From A KR@21:1/5 to All on Wed May 24 06:37:31 2023
    It is perfectly explained in the standards here [1] saying that:

    <quote>
    In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).
    </quote>

    Therefore, I wrote a code following what the standard says:

    <code>
    class Sample():
    def __init__(self):
    self.a = -10

    def __getattribute__(self, name):
    if name == 'a':
    return object.__getattribute__(self, name)

    raise AttributeError()

    s = Sample()
    result = s.a
    print(result)
    </code>
    I did not fall into recursion, and the output was
    -10

    I used here object.__getattribute__(self, name) cause the base class of Sample is object.

    If I derive the Sample class from another class such as A, I should change object.__getattribute__(self, name) to A.__getattribute__(self, name) as the base class of class Sample is class A.

    <code>
    class A:
    pass

    class Sample(A):
    def __init__(self):
    self.a = -10

    def __getattribute__(self, name):
    if name == 'a':
    return A.__getattribute__(self, name)

    raise AttributeError()

    s = Sample()

    result = s.a
    print(result)
    </code>
    which gives the same output as expected. No recursion and -10.

    However, when I try the code without deriving from a class:

    class AnyClassNoRelation:
    pass

    class Sample():
    def __init__(self):
    self.a = -10

    def __getattribute__(self, name):
    if name == 'a':
    return AnyClassNoRelation.__getattribute__(self, name)

    raise AttributeError()

    s = Sample()

    result = s.a
    print(result)
    and calling __getattribute__ via any class (in this example class AnyClassNoRelation) instead of object.__getattribute__(self, name) as the standard says call using the base class, I get the same output: no recursion and -10.

    So my question:

    How come this is possible (having the same output without using the base class's __getattribute__? Although the standards clearly states that __getattribute__ should be called from the base class.

    <quote>
    In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).
    </quote>

    Literally, I can call __getattribute__ with anyclass (except Sample cause it will be infinite recursion) I define and it works just fine. Could you explain me why that happens?


    [1] https://docs.python.org/3/reference/datamodel.html#object.__getattribute__

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Peter Otten@21:1/5 to A KR on Thu May 25 09:59:03 2023
    On 24/05/2023 15:37, A KR wrote:
    It is perfectly explained in the standards here [1] saying that:

    <quote>
    In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).
    </quote>

    Therefore, I wrote a code following what the standard says:

    <code>
    class Sample():
    def __init__(self):
    self.a = -10

    def __getattribute__(self, name):
    if name == 'a':
    return object.__getattribute__(self, name)

    raise AttributeError()

    s = Sample()
    result = s.a
    print(result)
    </code>
    I did not fall into recursion, and the output was
    -10

    While this works it's not how I understand the recommended pattern. I'd
    rather treat "special" attributes first and then use the
    __getattribute__ method of the base class as a fallback:

    class Demo:
    def __getattribute__(self, name):
    if name == "answer":
    return 42
    return super().__getattribute__(name)

    That way your special arguments,

    d = Demo()
    d.answer
    42


    missing arguments

    d.whatever
    Traceback (most recent call last):
    File "<pyshell#13>", line 1, in <module>
    d.whatever
    File "<pyshell#10>", line 5, in __getattribute__
    return super().__getattribute__(name)
    AttributeError: 'Demo' object has no attribute 'whatever'

    and "normal" arguments are treated as expected

    d.question = "What's up?"
    d.question
    "What's up?"

    Eventual "special" arguments in the superclass would also remain accessible.


    However, when I try the code without deriving from a class:

    class AnyClassNoRelation:
    pass

    class Sample():
    def __init__(self):
    self.a = -10

    def __getattribute__(self, name):
    if name == 'a':
    return AnyClassNoRelation.__getattribute__(self, name)

    raise AttributeError()

    s = Sample()

    result = s.a
    print(result)
    and calling __getattribute__ via any class (in this example class AnyClassNoRelation) instead of object.__getattribute__(self, name) as the standard says call using the base class, I get the same output: no recursion and -10.

    So my question:

    How come this is possible (having the same output without using the base class's __getattribute__? Although the standards clearly states that __getattribute__ should be called from the base class.


    AnyClassNoRelation does not override __getattribute__, so

    AnyClassNoRelation.__getattribute__ is object.__getattribute__
    True


    There is no sanity check whether a method that you call explicitly is
    actually in an object's inheritance tree,

    class NoRelation:
    def __getattribute__(self, name):
    return name.upper()


    class Demo:
    def __getattribute__(self, name):
    return "<{}>".format(NoRelation.__getattribute__(self, name))


    Demo().some_arg
    '<SOME_ARG>'

    but the only purpose I can imagine of actually calling "someone else's"
    method is to confuse the reader...
    <quote>
    In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).
    </quote>

    Literally, I can call __getattribute__ with anyclass (except Sample cause it will be infinite recursion) I define and it works just fine. Could you explain me why that happens?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)