Подтвердить что ты не робот

Какая магия python выполняет dir() с __getattr__?

Ниже в python 2.7 с MySQLdb 1.2.3.

Мне понадобилась оболочка класса, чтобы добавить некоторые атрибуты к объектам, которые ее не поддерживали (классы с __slots__ и/или некоторым классом, написанным на языке C), поэтому я получил что-то вроде этого:

class Wrapper(object):

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

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

Я ожидал, что встроенный dir(), вызванный моим экземпляром Wrapper, должен был вернуть только имена, наследуемые объектом plus wrapped_obj, и я обнаружил, что это на самом деле имеет место для большинства случаев, но не для все. Я пробовал это с помощью обычного старого класса стиля, пользовательского нового класса стилей и некоторых встроенных классов, он всегда работал так: единственное исключение, которое я обнаружил, - это когда обернутый объект был экземпляром класса _mysql.connection. В этом случае dir() на моем объекте также известно все имена методов, прикрепленные к обернутому объекту связи.

Я читал в документации python о dir, и это поведение кажется законным: dir должен возвращать список "интересных имен", а не "реальное" содержимое экземпляра. Но я действительно не могу понять, как он это делает: он действительно понимает реализацию моего __getattr__ и разрешает прикрепленный элемент? Если это так, то почему только с этим connection классом, а не с более простым dict?

Вот пример вложенного кода в качестве примера этого любопытного поведения:

>>> 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']
>>> 
4b9b3361

Ответ 1

В Python 2 есть два устаревших атрибута, object.__members__ и object.__methods__; они были нацелены на поддержку dir() в типах расширений (C-определенные объекты):

object.__methods__
Устаревший с версии 2.2: используйте встроенную функцию dir(), чтобы получить список атрибутов объектов. Этот атрибут больше не доступен.

object.__members__
Устаревший с версии 2.2: Используйте встроенную функцию dir() для получения списка атрибутов объектов. Этот атрибут больше не доступен.

Они были удалены из Python 3, но поскольку ваш объект соединения (по крайней мере, в старой версии, который вы используете), по-прежнему содержит атрибут __methods__, который найден через ваш крюк __getattr__ и используется здесь dir() здесь.

Если вы добавили оператор print к методу __getattr__, вы увидите доступные атрибуты:

>>> 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']

Для объектов нового стиля новый метод __dir__, поддерживаемый dir(), правильно просматривается по типу только для того, чтобы вы не Я вижу, что здесь можно получить доступ.

Файл