Python中dir(…)和vars(…).keys()有什么区别?

92 投票
3 回答
26517 浏览
提问于 2025-04-15 12:10

在Python中,dir(…)vars(…).keys()之间有区别吗?

(我希望它们之间有区别,因为如果没有,这就违背了“只用一种方式来做”的原则…… :)

3 个回答

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
31

文档中对 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() 的输出则是按照属性定义的顺序排列的。

130

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

撰写回答