简体   繁体   English

dir()用__getattr__执行什么样的python魔术?

[英]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 function dir() 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM