[英]What kind of python magic does dir() perform with __getattr__?
The following is in python 2.7 with MySQLdb 1.2.3. 以下是使用MySQLdb 1.2.3的python 2.7。
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: 我需要一个类包装器来向不支持它的对象添加一些属性(带有__slots__
类和/或用C语言编写的一些类)所以我得到了类似这样的东西:
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. 我期待在我的Wrapper
实例上调用的dir()
内置应该只返回object plus wrapped_obj
继承的名称,并且我发现大多数情况下都是这种情况,但并非所有情况都是如此。 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
. 我尝试使用自定义旧样式类,自定义新样式类和一些内置类,它总是以这种方式工作:我发现的唯一例外是当包装对象是类_mysql.connection
的实例时。 In this case, dir()
on my object happens to know also all the method names attached to the wrapped connection object. 在这种情况下,我的对象上的dir()
碰巧也知道附加到包装的连接对象的所有方法名称。
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. 我在python文档中读到有关dir
,这种行为似乎是合法的: dir
应该返回一个“有趣的名字”列表,而不是实例的“真实”内容。 But I really can't figure how it does this: it actually understands the implementation of my __getattr__
and resolves to the attached item? 但我真的无法弄清楚它是如何做到的:它实际上理解我的__getattr__
的实现并解析为附加项目? If this is true, why only with that connection
class and not for instance with a simpler dict
? 如果这是真的,为什么只有那个connection
类而不是例如更简单的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']
>>>
There are two deprecated attributes in Python 2, object.__members__
and object.__methods__
; Python 2中有两个不推荐使用的属性, object.__members__
和object.__methods__
; these were aimed at supporting dir()
on extension types (C-defined objects): 这些旨在支持扩展类型(C定义的对象)上的dir()
):
object.__methods__
Deprecated since version 2.2: Use the built-in functiondir()
to get a list of an object's attributes. 自2.2版以来不推荐使用:使用内置函数dir()
获取对象属性的列表。 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. 自2.2版以来不推荐使用:使用内置函数dir()获取对象属性的列表。 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. 这些已从Python 3中删除,但因为您的连接对象(至少在您使用的旧版本中)仍提供__methods__
属性,该属性可通过__getattr__
钩子找到并由dir()
在此处使用。
If you add a print
statement to the __getattr__
method you'll see the attributes being accessed: 如果向__getattr__
方法添加print
语句,您将看到正在访问的属性:
>>> 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. 对于新式对象, dir()
支持的较新的__dir__
方法只能在类型上正确查找,因此您不会在此处看到要访问的方法。
The project HISTORY file suggests the attributes were removed in the big Python 3 compatibility update for 1.2.4 beta 1. 项目HISTORY文件建议在1.2.4 beta 1的大Python 3兼容性更新中删除属性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.