1

I came across this piece of code, and I am surprised that it adds an element to the list.

Here's the code:

class Wrapper:
    def __init__(self, object):
        self.wrapped = object
    def __getattr__(self,attrname):
        print('Trace: '+attrname)
        return getattr(self.wrapped,attrname)

X = Wrapper([1,2,3])
X.append(4)
print(X.wrapped)

I am surprised because if I run type(X), I get __main__.Wrapper, which makes sense because X is an object of class Wrapper. Hence, I am unsure why X.append adds to the list in the attribute wrapped directly. After all, the type of X isn't list but Wrapper.

Shouldn't the call have been X.wrapped.append(4)? This works as well.

I am a beginner, and this might be a basic question. I'd appreciate any thoughts, and thanks for any help.


This code was adopted from Mark Lutz's book. I am using Anaconda 3.6 distribution

2
  • 1
    The existing call is X.__getattr__('append')(4). Commented Jul 15, 2018 at 5:39
  • Firstly, Object is a built-in name. Don't use it as your variable names. Secondly, It's because self.wrapped is a list and __getattr__ is calling the list's append method. Commented Jul 15, 2018 at 5:39

2 Answers 2

1

This class is just adding a print statement before the original attribute of the object is accessed.

To understand how this works, you have to understand a bit about what __getattr__ does. The documentation for these methods is under the data model section of the Python manual:

Called when the default attribute access fails with an AttributeError (either __getattribute__() raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self; or __get__() of a name property raises AttributeError). This method should either return the (computed) attribute value or raise an AttributeError exception.

The . (dot) operator is really a search or lookup operator, when you write a statement like this:

foo.append(x)

You and I understand this to mean "call the append method of the foo object, with the argument x" - Python is trying to search for an attribute "append" on the foo object, then trying to call it with an argument of x.

In the custom object, we don't have an append attribute, which triggers the __getattr__ call.

We then intercept this call to __getattr__, print out a debug statement, and then pass on this call to the actual object (a reference to it is in self.wrapped).

In short, we are saying - hey - we understand that our wrapper object doesn't have this property, but try and see if the wrapped object does, by calling its __getattr__ instead.

This concept of passing along the call up the chain allows flexibility in that we mimic what the original (wrapped) object would do - making our wrapper very generic. It would work even if you passed in a string or any other Python object.

Sign up to request clarification or add additional context in comments.

Comments

0

The __getattr__ is an operator overload magic method for the attribute reference . operator. There are several other ways to do this, like descriptors for single attributes or __getattribute__ that overrides even existing attributes.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.