Ниже в 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']
>>>