Python中dir(…)和vars(…).keys()有什么区别?
在Python中,dir(…)
和vars(…).keys()
之间有区别吗?
(我希望它们之间有区别,因为如果没有,这就违背了“只用一种方式来做”的原则…… :)
3 个回答
除了已有的回答,我想补充一点,使用vars()来处理内置类型的实例时会出错,因为内置类型的实例没有__dict__
这个属性。
比如:
In [96]: vars([])
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-96-a6cdd8d17b23> in <module>()
----> 1 vars([])
TypeError: vars() argument must have __dict__ attribute
文档中对 dir
的说明是:
如果不传任何参数,它会返回当前作用域中的名称列表。如果传入一个参数,它会尝试返回那个对象的有效属性列表。
而对 vars
的说明是:
如果不传任何参数,它会返回一个字典,表示当前的本地符号表。如果传入一个模块、类或类实例对象(或者其他有
__dict__
属性的东西),它会返回一个字典,表示该对象的符号表。
如果你还看不出区别,下面的内容可能会更清楚(为了更容易阅读而分组):
>>> dir(list)
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__',
'__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__',
'__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__',
'__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend',
'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> vars(list).keys()
dict_keys(['__repr__',
'__hash__',
'__getattribute__',
'__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__',
'__iter__',
'__init__',
'__len__',
'__getitem__', '__setitem__', '__delitem__',
'__add__', '__mul__', '__rmul__', '__contains__', '__iadd__', '__imul__',
'__new__',
'__reversed__', '__sizeof__',
'clear', 'copy', 'append', 'insert', 'extend', 'pop', 'remove', 'index', 'count', 'reverse', 'sort',
'__class_getitem__',
'__doc__'])
如果你不想仔细阅读,dir
包含这些属性,而 vars
则不包含:
>>> set(dir(list)) - vars(list).keys()
{'__class__', '__delattr__', '__dir__', '__format__', '__init_subclass__',
'__reduce__', '__reduce_ex__', '__setattr__', '__str__', '__subclasshook__'}
还要注意的是,dir()
的输出是按字母顺序排列的,而 vars()
的输出则是按照属性定义的顺序排列的。
在Python中,通常对象会把它的实例变量存在一个属于它自己的字典里(除了使用slots的情况)。你可以用vars(x)
来获取这个字典(x.__dict__
也可以)。而dir(x)
则返回的是一个字典,里面包含了x
的“属性、它所在类的属性,以及它的父类的属性”。
当你用点操作符访问一个对象的属性时,Python做的事情比单纯在对象的字典里查找属性要复杂得多。比如说,假设x
是类C
的一个实例,你想调用它的方法m
:
class C:
def m(self):
print("m")
x = C()
x.m()
这里的方法m
并不在x.__dict__
里。它是类C
的一个属性。
当你调用x.m()
时,Python会先在x.__dict__
里查找m
,但找不到。不过,Python知道x
是类C
的实例,所以接下来会在C.__dict__
里查找,最终找到m
并用x
作为第一个参数来调用它。
所以,vars(x)
和dir(x)
的区别在于,dir(x)
会额外查找x
所在类(以及它的父类)中的属性,而不仅仅是x
自己字典里的属性。在上面的例子中,vars(x)
返回的是一个空字典,因为x
没有实例变量。但是,dir(x)
返回的是
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__le__', '__lt__', '__module__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'm']