Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What kind of python magic does dir() perform with __getattr__?

The following is in python 2.7 with MySQLdb 1.2.3.

I needed a class wrapper to add some attributes to objects which didn't support it (classes with __slots__ and/or some class written in C) so I came out with something like this:

class Wrapper(object):

    def __init__(self, obj):
        self._wrapped_obj = obj

    def __getattr__(self, obj):
        return getattr(self._wrapped_obj, attr)

I was expecting that the dir() builtin called on my instance of Wrapper should have returned just the names inherited by object plus wrapped_obj, and I discovered that this is actually the case for most cases, but not for all. I tried this with a custom old style class, a custom new style class, and some builtin classes, it always worked this way: the only exception that i found is when the wrapped object was an instance of the class _mysql.connection. In this case, dir() on my object happens to know also all the method names attached to the wrapped connection object.

I read in the python documentation about dir, and this behaviour appears to be legit: dir is supposed to return a list of "interesting names", not the "real" content of the instance. But I really can't figure how it does this: it actually understands the implementation of my __getattr__ and resolves to the attached item? If this is true, why only with that connection class and not for instance with a simpler dict?

Here is some pasted code as an example of this curious behaviour:

>>> from _mysql import connection
>>> c = connection(**connection_parameters)
>>> c
<_mysql.connection open to '127.0.0.1' at a16920>
>>> 
>>> dir(c)
['affected_rows', 'autocommit', 'change_user', 'character_set_name', 'close', 'commit', 'dump_debug_info', 'errno', 'error', 'escape', 'escape_string', 'field_count', 'get_character_set_info', 'get_host_info', 'get_proto_info', 'get_server_info', 'info', 'insert_id', 'kill', 'next_result', 'ping', 'query', 'rollback', 'select_db', 'set_character_set', 'set_server_option', 'shutdown', 'sqlstate', 'stat', 'store_result', 'string_literal', 'thread_id', 'use_result', 'warning_count']
>>> 
>>> w = Wrapper(c)
>>> dir(w)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_wrapped_obj', 'affected_rows', 'autocommit', 'change_user', 'character_set_name', 'close', 'commit', 'dump_debug_info', 'errno', 'error', 'escape', 'escape_string', 'field_count', 'get_character_set_info', 'get_host_info', 'get_proto_info', 'get_server_info', 'info', 'insert_id', 'kill', 'next_result', 'ping', 'query', 'rollback', 'select_db', 'set_character_set', 'set_server_option', 'shutdown', 'sqlstate', 'stat', 'store_result', 'string_literal', 'thread_id', 'use_result', 'warning_count']
>>> 
>>> d = Wrapper({})
>>> dir(d)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_wrapped_obj']
>>> 
like image 480
giuliocatte Avatar asked Dec 19 '14 13:12

giuliocatte


People also ask

What does __ Getattr __ do in Python?

__getattr__Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self ). name is the attribute name. This method should return the (computed) attribute value or raise an AttributeError exception.

Is __ init __ a magic method?

Dunder or magic methods in Python are the methods having two prefix and suffix underscores in the method name. Dunder here means “Double Under (Underscores)”. These are commonly used for operator overloading. Few examples for magic methods are: __init__, __add__, __len__, __repr__ etc.

What is a magic function in Python?

Magic methods in Python are the special methods that start and end with the double underscores. They are also called dunder methods. Magic methods are not meant to be invoked directly by you, but the invocation happens internally from the class on a certain action.

What is Getattr and Setattr in Python?

Python setattr() and getattr() goes hand-in-hand. As we have already seen what getattr() does; The setattr() function is used to assign a new value to an object/instance attribute. Syntax. Use a different Browser.

What is the __getattr__ method in Python?

object. __getattr__ (self, name) Is an object method that is called if the object’s properties are not found. This method should return the property value or throw AttributeError. Note that if the object property can be found through the normal mechanism, it will not be called. __getattr__ method. class Frob:... def __init__ (self, bamf):...

What is Python magic method?

In a nutshell magic method do magic to python programming by reducing complexity. This is a guide on Python Magic Method. Here we discuss the introduction to Python Magic Method, it’s components and advantages, as well as some examples.

What is the __getattr__ magic method?

But using the __ getattr__ magic method, we can intercept that inexistent attribute lookup and do something so it doesn’t fail: But if the attribute does exist, __getattr__ won’t be invoked:

What are the magic methods surrounded by double underscores in Python?

As you can see above, the int class includes various magic methods surrounded by double underscores. For example, the __add__ method is a magic method which gets called when we add two numbers using the + operator. Consider the following example.


1 Answers

There are two deprecated attributes in Python 2, object.__members__ and object.__methods__; these were aimed at supporting dir() on extension types (C-defined objects):

object.__methods__
Deprecated since version 2.2: Use the built-in function dir() to get a list of an object’s attributes. This attribute is no longer available.

object.__members__
Deprecated since version 2.2: Use the built-in function dir() to get a list of an object’s attributes. This attribute is no longer available.

These were removed from Python 3, but because your connection object (at leasts in the older version you are using) still provides a __methods__ attribute that is found through your __getattr__ hook and used by dir() here.

If you add a print statement to the __getattr__ method you'll see the attributes being accessed:

>>> class Wrapper(object):
...     def __init__(self, obj):
...         self._wrapped_obj = obj
...     def __getattr__(self, obj):
...         print 'getattr', obj
...         return getattr(self._wrapped_obj, attr)
... 
>>> dir(Wrapper({}))
getattr __members__
getattr __methods__
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_wrapped_obj']

For new-style objects, the newer __dir__ method supported by dir() is properly looked up on the type only so you don't see that being accessed here.

The project HISTORY file suggests the attributes were removed in the big Python 3 compatibility update for 1.2.4 beta 1.

like image 90
Martijn Pieters Avatar answered Oct 23 '22 17:10

Martijn Pieters