当前位置: 首页 > web >正文

python:vars()方法

python:vars()方法

1 前言

vars() 函数返回对象的__dict__ 属性的字典。这个字典包含了对象的所有属性和它们的值。vars() 函数可以用于模块、类、实例,或者拥有__dict__ 属性的任何其它对象。

参考Python官网如下:

https://docs.python.org/zh-cn/3/library/functions.html#vars

Python官网解释:

vars()

vars(object)

返回模块、类、实例或任何其他具有__dict__属性的对象的__dict__属性。

模块和实例这样的对象具有可更新的__dict__属性;但是,其他对象的__dict__属性可能会设置写入限制(例如,类会使用 types.MappingProxyType 来防止直接更新字典)。

不带参数时,vars() 的行为将类似于 locals()。

如果指定了一个对象但它没有__dict__属性(例如,当它所属的类定义了__slots__属性时)则会引发 TypeError 异常。

在 3.13 版本发生变更: 不带参数调用此函数的结果已被更新为与 locals() 内置函数的描述类似。

2 使用

本文基于Python 3.9进行Python vars() 方法使用详解。

2.1 函数概述

vars() 是 Python 内置函数,用于返回对象的 __dict__ 属性。若无参数传入,则返回当前作用域(局部命名空间)的变量字典。其核心功能是通过字典形式展示对象的属性和值,或当前作用域的变量集合。

2.2 函数语法与参数

vars([object]) 
  • ‌参数‌:

object:可选,支持模块、类实例或其他具有__dict__ 属性的对象。

  • ‌返回值‌:

若传入对象,返回该对象的属性字典;若未传参数,返回当前局部作用域的变量字典。

2.3 核心功能

  • ‌获取对象属性字典‌

vars() 可查看类实例的属性及值,适用于调试或动态分析:

class Person:def __init__(self, name, age):self.name = nameself.age = agedef run(self):pass
p = Person("Xiaoxu&li", 25)
print(vars(p))  # 输出:{'name': 'Xiaoxu&li', 'age': 25}

此处注意:不是所有的实例对象都可以使用vars方法打印其__dict__属性,比如含有的__slots__的类所定义的实例对象:

class Apple:__slots__ = ()def __init__(self):passa = Apple()# 这个是可以打印的
print(vars(Apple))
print(vars(a))  # 报错:TypeError: vars() argument must have __dict__ attribute

但是我们知道,类的__slots__虽然定义后会导致类实例对象缺失__dict__属性,但在__slots__声明中是可以再次显式定义__dict__的,下述将不会抛出异常:

class Apple:__slots__ = {"__dict__": {"name": "xiaoli"}}def __init__(self):passa = Apple()# 下述两个语句都将不再报错
print(vars(Apple))
print(vars(a)

结果正常执行,不再抛出异常:

{'__module__': '__main__', '__slots__': {'__dict__': {'name': 'xiaoli'}}, '__init__': <function Apple.__init__ at 0x01AE8B68>, '__dict__': <attribute '__dict__' of 'Apple' objects>, '__doc__': None}
{}

针对上述的修改,即便将__slots__修改为如下示例,也不会使得vars方法抛出异常,也即是说,只要__slots__中具有__dict__属性即可,即便为空也不会产生影响:

class Apple:__slots__ = {"__dict__": None}...
  • ‌查看模块或类的变量‌

‌模块属性‌:

import mathprint(vars(math))
print(math.__dict__)
print(id(vars(math)))
print(id(math.__dict__))  # 返回模块内定义的变量、函数等字典  

结果如下:

{'__name__': 'math', '__doc__': 'This module provides access to the mathematical functions\ndefined by the C standard.', '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='math', loader=<class '_frozen_importlib.BuiltinImporter'>, origin='built-in'), 'acos': <built-in function acos>, 'acosh': <built-in function acosh>, 'asin': <built-in function asin>, 'asinh': <built-in function asinh>, 'atan': <built-in function atan>, 'atan2': <built-in function atan2>, 'atanh': <built-in function atanh>, 'ceil': <built-in function ceil>, 'copysign': <built-in function copysign>, 'cos': <built-in function cos>, 'cosh': <built-in function cosh>, 'degrees': <built-in function degrees>, 'dist': <built-in function dist>, 'erf': <built-in function erf>, 'erfc': <built-in function erfc>, 'exp': <built-in function exp>, 'expm1': <built-in function expm1>, 'fabs': <built-in function fabs>, 'factorial': <built-in function factorial>, 'floor': <built-in function floor>, 'fmod': <built-in function fmod>, 'frexp': <built-in function frexp>, 'fsum': <built-in function fsum>, 'gamma': <built-in function gamma>, 'gcd': <built-in function gcd>, 'hypot': <built-in function hypot>, 'isclose': <built-in function isclose>, 'isfinite': <built-in function isfinite>, 'isinf': <built-in function isinf>, 'isnan': <built-in function isnan>, 'isqrt': <built-in function isqrt>, 'lcm': <built-in function lcm>, 'ldexp': <built-in function ldexp>, 'lgamma': <built-in function lgamma>, 'log': <built-in function log>, 'log1p': <built-in function log1p>, 'log10': <built-in function log10>, 'log2': <built-in function log2>, 'modf': <built-in function modf>, 'pow': <built-in function pow>, 'radians': <built-in function radians>, 'remainder': <built-in function remainder>, 'sin': <built-in function sin>, 'sinh': <built-in function sinh>, 'sqrt': <built-in function sqrt>, 'tan': <built-in function tan>, 'tanh': <built-in function tanh>, 'trunc': <built-in function trunc>, 'prod': <built-in function prod>, 'perm': <built-in function perm>, 'comb': <built-in function comb>, 'nextafter': <built-in function nextafter>, 'ulp': <built-in function ulp>, 'pi': 3.141592653589793, 'e': 2.718281828459045, 'tau': 6.283185307179586, 'inf': inf, 'nan': nan}
{'__name__': 'math', '__doc__': 'This module provides access to the mathematical functions\ndefined by the C standard.', '__package__': '', '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': ModuleSpec(name='math', loader=<class '_frozen_importlib.BuiltinImporter'>, origin='built-in'), 'acos': <built-in function acos>, 'acosh': <built-in function acosh>, 'asin': <built-in function asin>, 'asinh': <built-in function asinh>, 'atan': <built-in function atan>, 'atan2': <built-in function atan2>, 'atanh': <built-in function atanh>, 'ceil': <built-in function ceil>, 'copysign': <built-in function copysign>, 'cos': <built-in function cos>, 'cosh': <built-in function cosh>, 'degrees': <built-in function degrees>, 'dist': <built-in function dist>, 'erf': <built-in function erf>, 'erfc': <built-in function erfc>, 'exp': <built-in function exp>, 'expm1': <built-in function expm1>, 'fabs': <built-in function fabs>, 'factorial': <built-in function factorial>, 'floor': <built-in function floor>, 'fmod': <built-in function fmod>, 'frexp': <built-in function frexp>, 'fsum': <built-in function fsum>, 'gamma': <built-in function gamma>, 'gcd': <built-in function gcd>, 'hypot': <built-in function hypot>, 'isclose': <built-in function isclose>, 'isfinite': <built-in function isfinite>, 'isinf': <built-in function isinf>, 'isnan': <built-in function isnan>, 'isqrt': <built-in function isqrt>, 'lcm': <built-in function lcm>, 'ldexp': <built-in function ldexp>, 'lgamma': <built-in function lgamma>, 'log': <built-in function log>, 'log1p': <built-in function log1p>, 'log10': <built-in function log10>, 'log2': <built-in function log2>, 'modf': <built-in function modf>, 'pow': <built-in function pow>, 'radians': <built-in function radians>, 'remainder': <built-in function remainder>, 'sin': <built-in function sin>, 'sinh': <built-in function sinh>, 'sqrt': <built-in function sqrt>, 'tan': <built-in function tan>, 'tanh': <built-in function tanh>, 'trunc': <built-in function trunc>, 'prod': <built-in function prod>, 'perm': <built-in function perm>, 'comb': <built-in function comb>, 'nextafter': <built-in function nextafter>, 'ulp': <built-in function ulp>, 'pi': 3.141592653589793, 'e': 2.718281828459045, 'tau': 6.283185307179586, 'inf': inf, 'nan': nan}
31089920
31089920

类属性‌(有区别于上述的类self实例属性):

class MyClass:  class_var = "Class Variable"  
print(vars(MyClass))  # 包含类变量 `class_var` 及其他内置属性

结果如下:

{'__module__': '__main__', 'class_var': 'Class Variable', '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
  • ‌动态修改对象属性‌

上述表明,vars方法获取的__dict__不是拷贝出来的副本,而是同一个引用对象,故可通过修改返回的字典,动态调整对象属性:

obj = MyClass()  
obj_dict = vars(obj)  
obj_dict["new_attr"] = "Dynamic Value"  
print(obj.new_attr)  # 输出:Dynamic Value

再来看一个演示例子:

a = 12
b = Falseclass Apple:def __init__(self, name, price):self.name = nameself.price = priceprint(vars())
print(vars(Apple))
print(Apple.__dict__)
print(id(vars(Apple)))
print(id(Apple.__dict__))# AttributeError: type object 'Apple' has no attribute 'a'
# print(Apple.a)
# dict_data = vars(Apple)
# dict_data["a"] = 88
# print(dict_data.a)
# 上述的方式依然会报错:
# TypeError: 'mappingproxy' object does not support item assignmentapple = Apple("apple", 2.7)
dict_data2 = vars(apple)
dict_data2["a"] = 77
print(dict_data2)
print(apple.a)

执行结果:

{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00B1F478>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:\\ptest\\test\\TestVars.py', '__cached__': None, 'a': 12, 'b': False, 'Apple': <class '__main__.Apple'>}
{'__module__': '__main__', '__init__': <function Apple.__init__ at 0x00B68B68>, '__dict__': <attribute '__dict__' of 'Apple' objects>, '__weakref__': <attribute '__weakref__' of 'Apple' objects>, '__doc__': None}
{'__module__': '__main__', '__init__': <function Apple.__init__ at 0x00B68B68>, '__dict__': <attribute '__dict__' of 'Apple' objects>, '__weakref__': <attribute '__weakref__' of 'Apple' objects>, '__doc__': None}
11983432
11983432
{'name': 'apple', 'price': 2.7, 'a': 77}
77

由python官网可知:模块和实例这样的对象具有可更新的__dict__属性;但是,其他对象的__dict__属性可能会设置写入限制(例如,类会使用 types.MappingProxyType 来防止直接更新字典)。故而上述通过对vars(Apple)来直接修改类的__dict__属性,会抛出TypeError错误。

TypeError: ‘mappingproxy’ object does not support item assignment错误是由于尝试修改Python的types.MappingProxyType对象导致的。该类型是字典的只读视图,底层字典的修改需通过原始可变字典实现。

(一)、原因分析

  • ‌只读特性‌

MappingProxyType 通过封装普通字典实现只读访问,其底层数据修改只能通过原字典完成。

  • ‌设计用途‌

常用于保护配置数据、类属性等场景,防止意外修改。

(二)、解决方案

方法 1:修改原始字典后重新生成只读视图

import typesoriginal_dict = {'key1': 'value1'}
read_only_dict = types.MappingProxyType(original_dict)# 错误操作:read_only_dict['key2'] = 'value2'  # 触发TypeError# 正确操作:修改原字典后重新生成只读视图
original_dict['key2'] = 'value2'
new_read_only_dict = types.MappingProxyType(original_dict)

方法 2:创建新可变字典进行修改

若无法访问原字典,可通过复制数据到新字典实现修改:

modified_dict = dict(read_only_dict)  # 转换为普通字典
modified_dict['new_key'] = 'new_value'
read_only_dict = types.MappingProxyType(modified_dict)

方法 3:使用不可变数据类型替代

若需完全不可变结构,可改用 frozendict 等第三方库:

from frozendict import frozendictimmutable_dict = frozendict({'key': 'value'})

(三)、设计思路

  • ‌分层封装‌

在模块或类内部维护可变字典,对外暴露 MappingProxyType 对象。

  • ‌明确权限控制‌

通过工厂函数控制只读字典的生成过程:

def create_protected_config(config_data):_internal_config = dict(config_data)return types.MappingProxyType(_internal_config)

(四)、扩展类比

此问题与以下错误类型原理相似(均因操作不可变对象引发):

TypeError: ‘str’ object does not support item assignment → 字符串转列表修改
TypeError: ‘tuple’ object does not support item assignment → 元组转列表修改

(五)、自定义Mapping使用MappingProxyType

print("开始使用只读视图:")
from types import MappingProxyType
from typing import Mappingclass CustomMapping(Mapping):def __init__(self, data):self._data = datadef __getitem__(self, key):return self._data[key]def __iter__(self):return iter(self._data)def __len__(self):return len(self._data)# TypeError: Can't instantiate abstract class Mapping with abstract methods __getitem__, __iter__, __len__
# 直接实例化mappings = Mapping()会报错:
# mappings = Mapping()# 使用 issubclass() 检查继承关系:
print(issubclass(CustomMapping, Mapping))  # 应输出 Truemappings = CustomMapping({"a": 1, "b": 2})
mappingProxy = MappingProxyType(mappings)
print(mappingProxy)

结果:

开始使用只读视图:
True
<__main__.CustomMapping object at 0x01FFDAC0>

也可以改为使用collections.abc模块下的Mapping抽象类,可以通过Mapping.__abstractmethods__获取其抽象方法:

print("开始使用只读视图:")
from types import MappingProxyType
from collections.abc import Mappingclass CustomMapping(Mapping):def __init__(self, data):self._data = datadef __getitem__(self, key):return self._data[key]def __iter__(self):return iter(self._data)def __len__(self):return len(self._data)# TypeError: Can't instantiate abstract class Mapping with abstract methods __getitem__, __iter__, __len__
# 直接实例化mappings = Mapping()会报错:
# mappings = Mapping()# 使用 issubclass() 检查继承关系:
print(issubclass(CustomMapping, Mapping))  # 应输出 Truemappings = CustomMapping({"a": 1, "b": 2})
mappingProxy = MappingProxyType(mappings)
print(mappingProxy)
print(Mapping.__abstractmethods__)
print(dir(Mapping))

结果:

开始使用只读视图:
True
<__main__.CustomMapping object at 0x10318cfa0>
frozenset({'__iter__', '__getitem__', '__len__'})
['__abstractmethods__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_abc_impl', 'get', 'items', 'keys', 'values']
  • ‌ ‌查看具有描述器对象的类属性‌

参考Python官网对于描述器的说明:

https://docs.python.org/zh-cn/3/howto/descriptor.html#descriptorhowto

在Python中,具有__get__、__set__或者__set_name__方法的类,都可以称为描述器。要使用描述器,它必须作为一个类变量存储在另一个类中(注意,这里描述器必须是作为类变量而非实例变量)。而vars方法可以查看类变量和实例变量,下面将对含有描述器的类下使用vars进行分析:

在类A中,y为描述器类属性,则对类A实例对象a.y 查找中,点运算符会根据描述器实例的 __get__ 方法将其识别出来,调用该方法并返回。

描述器的一种流行用法是管理对实例数据的访问。 描述器被分配给类字典中的公有属性,而实际数据则作为私有属性存储在实例字典中。 描述器的__get__() 和__set__() 方法会在公有属性被访问时被触发。

在下面的例子中,age 是公开属性,_age 是私有属性。当访问公开属性时,描述器会记录下查找或更新的日志:

import logging# logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',handlers=[logging.FileHandler('app.log'), logging.StreamHandler()]
)# logger = logging.getLogger(__name__)class LoggedAgeAccess:def __set_name__(self, owner, name):logging.info('Creating %r from %r', name, owner)def __get__(self, obj, objtype=None):value = obj._agelogging.info('Accessing %r giving %r', 'age', value)return valuedef __set__(self, obj, value):logging.info('Updating %r to %r', 'age', value)obj._age = valueclass Person:age = LoggedAgeAccess()  # 描述器实例def __init__(self, name, age):self.name = name  # 常规实例属性self.age = age  # 调用 __set__()def birthday(self):self.age += 1  # 调用 __get__() 和 __set__()mary = Person('Marry M', 30)
dave = Person('Xiao Li', 40)
print(vars(mary))
print(vars(Person))

结果:

2025-05-05 17:30:01,451 - root - INFO - Creating 'age' from <class '__main__.Person'>
2025-05-05 17:30:01,451 - root - INFO - Updating 'age' to 30
2025-05-05 17:30:01,451 - root - INFO - Updating 'age' to 40
{'name': 'Marry M', '_age': 30}
{'__module__': '__main__', 'age': <__main__.LoggedAgeAccess object at 0x01A2C070>, '__init__': <function Person.__init__ at 0x01A924F0>, 'birthday': <function Person.birthday at 0x01B46F58>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

上述结果可知,因官网提到:描述器被分配给类字典中的公有属性,而实际数据则作为私有属性存储在实例字典中,所以我们通过vars(Person)可以看到类字典中公有属性age,它是一个描述器对象实例,但是并没有获取到描述器为我们生成的私有属性_age;而vars(mary)则没有类字典的公有属性age,实例对象可以获取到私有属性_age。

核心代码不改动,新增代码如下:

mary = Person('Marry M', 30)
dave = Person('Xiao Li', 40)
print(vars(mary))
print(vars(Person))print(mary.age)
mary.birthday()
print(mary.name)

执行结果:

2025-05-05 18:01:32,846 - root - INFO - Creating 'age' from <class '__main__.Person'>
2025-05-05 18:01:32,847 - root - INFO - Updating 'age' to 30
2025-05-05 18:01:32,847 - root - INFO - Updating 'age' to 40
2025-05-05 18:01:32,847 - root - INFO - Accessing 'age' giving 30
2025-05-05 18:01:32,847 - root - INFO - Accessing 'age' giving 30
2025-05-05 18:01:32,847 - root - INFO - Updating 'age' to 31
{'name': 'Marry M', '_age': 30}
{'__module__': '__main__', 'age': <__main__.LoggedAgeAccess object at 0x00F3C9B8>, '__init__': <function Person.__init__ at 0x00FA24F0>, 'birthday': <function Person.birthday at 0x01056F58>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
30
Marry M

上述可知,常规属性查找不会被记录,比如name;而被描述器管理的属性比如age,是被会记录的。上述的日志打印,因为__set_name__方法在类中作为描述器定义的方法,多个实例也仅会调用一次,且是最先调用的;而描述器的__get__() 和__set__() 方法会在公有属性被访问时被触发(比如a.x的形式(x是描述器对象,同时x为类属性,a为类实例对象)会触发__get__() ,而a.x = XXX的形式会触发__set__()),所以日志打印是符合预期的。

最后的思考如果上述将描述器中的obj._age都修改为obj.age,这样是否可行呢?如果可行的话vars打印属性是什么样的呢?

答案是不行,也就无谓讨论vars的结果了。因为比如调用mary.age时,本质是调用描述器的__get__()方法,而__get__()方法又是调用的obj.age,这里我们知道,obj其实就是定义描述器对象age的所属类Person的实例对象,也就是mary,那么这里本质上就还是调用的mary.age,故而此处会导致进行递归调用,从而在达到python中的最大递归次数后抛出异常:RecursionError: maximum recursion depth exceeded while calling a Python object

也就是下述错误的修改方式

class LoggedAgeAccess:def __set_name__(self, owner, name):logging.info('Creating %r from %r', name, owner)def __get__(self, obj, objtype=None):# value = obj._agevalue = obj.agelogging.info('Accessing %r giving %r', 'age', value)return valuedef __set__(self, obj, value):logging.info('Updating %r to %r', 'age', value)# obj._age = valueobj.age = value

结果与上述分析一致,执行报错:

在这里插入图片描述

下来再来看下官网中提到的另外一个栗子:

import logginglogging.basicConfig(level=logging.INFO)class LoggedAccess:def __set_name__(self, owner, name):self.public_name = nameself.private_name = '_' + namedef __get__(self, obj, objtype=None):value = getattr(obj, self.private_name)logging.info('Accessing %r giving %r', self.public_name, value)return valuedef __set__(self, obj, value):logging.info('Updating %r to %r', self.public_name, value)setattr(obj, self.private_name, value)class Person:name = LoggedAccess()  # 第一个描述器实例age = LoggedAccess()  # 第二个描述器实例def __init__(self, name, age):self.name = name  # 调用第一个描述器self.age = age  # 调用第二个描述器def birthday(self):self.age += 1

此处调用 vars() 来查找描述器而不触发它:

print(vars(vars(Person)['name']))
print(vars(vars(Person)['age']))

结果:

{'public_name': 'name', 'private_name': '_name'}
{'public_name': 'age', 'private_name': '_age'}

新类会记录对name和age二者的访问,同时这两个Person实例只会包含私有名称:

a = Person('Xiaoxu', 30)
b = Person('Xiaoli', 20)
print(vars(a))
print(vars(b))

结果:

INFO:root:Updating 'name' to 'Xiaoxu'
INFO:root:Updating 'age' to 30
INFO:root:Updating 'name' to 'Xiaoli'
INFO:root:Updating 'age' to 20
{'_name': 'Xiaoxu', '_age': 30}
{'_name': 'Xiaoli', '_age': 20}

但是我们通过.的方式是可以分别获取到实例对象的公有和私有属性的:

print(b.name)
print(b.age)
print(b._name)
print(b._age)

结果如下:

INFO:root:Accessing 'name' giving 'Xiaoli'
INFO:root:Accessing 'age' giving 20
Xiaoli
20
Xiaoli
20

2.4 注意事项

  • ‌作用域差异‌

在全局作用域无参调用 vars() 时,等同于 globals();
在函数内部调用 vars(),则返回函数的局部变量字典。

  • ‌对象限制‌

仅当对象具有__dict__ 属性时,vars() 才生效。例如,基本数据类型(如 int、str)调用会报错(当然前面提到的实现了 __slots__的类且未声明__dict__ 属性的类实例使用vars方法同样也会报错)。

举例说明:

# 这样不会报错
print(vars(int))
# 这样会报错:TypeError: vars() argument must have __dict__ attribute
# print(vars(10))

结果:

{'__repr__': <slot wrapper '__repr__' of 'int' objects>, '__hash__': <slot wrapper '__hash__' of 'int' objects>, '__getattribute__': <slot wrapper '__getattribute__' of 'int' objects>, '__lt__': <slot wrapper '__lt__' of 'int' objects>, '__le__': <slot wrapper '__le__' of 'int' objects>, '__eq__': <slot wrapper '__eq__' of 'int' objects>, '__ne__': <slot wrapper '__ne__' of 'int' objects>, '__gt__': <slot wrapper '__gt__' of 'int' objects>, '__ge__': <slot wrapper '__ge__' of 'int' objects>, '__add__': <slot wrapper '__add__' of 'int' objects>, '__radd__': <slot wrapper '__radd__' of 'int' objects>, '__sub__': <slot wrapper '__sub__' of 'int' objects>, '__rsub__': <slot wrapper '__rsub__' of 'int' objects>, '__mul__': <slot wrapper '__mul__' of 'int' objects>, '__rmul__': <slot wrapper '__rmul__' of 'int' objects>, '__mod__': <slot wrapper '__mod__' of 'int' objects>, '__rmod__': <slot wrapper '__rmod__' of 'int' objects>, '__divmod__': <slot wrapper '__divmod__' of 'int' objects>, '__rdivmod__': <slot wrapper '__rdivmod__' of 'int' objects>, '__pow__': <slot wrapper '__pow__' of 'int' objects>, '__rpow__': <slot wrapper '__rpow__' of 'int' objects>, '__neg__': <slot wrapper '__neg__' of 'int' objects>, '__pos__': <slot wrapper '__pos__' of 'int' objects>, '__abs__': <slot wrapper '__abs__' of 'int' objects>, '__bool__': <slot wrapper '__bool__' of 'int' objects>, '__invert__': <slot wrapper '__invert__' of 'int' objects>, '__lshift__': <slot wrapper '__lshift__' of 'int' objects>, '__rlshift__': <slot wrapper '__rlshift__' of 'int' objects>, '__rshift__': <slot wrapper '__rshift__' of 'int' objects>, '__rrshift__': <slot wrapper '__rrshift__' of 'int' objects>, '__and__': <slot wrapper '__and__' of 'int' objects>, '__rand__': <slot wrapper '__rand__' of 'int' objects>, '__xor__': <slot wrapper '__xor__' of 'int' objects>, '__rxor__': <slot wrapper '__rxor__' of 'int' objects>, '__or__': <slot wrapper '__or__' of 'int' objects>, '__ror__': <slot wrapper '__ror__' of 'int' objects>, '__int__': <slot wrapper '__int__' of 'int' objects>, '__float__': <slot wrapper '__float__' of 'int' objects>, '__floordiv__': <slot wrapper '__floordiv__' of 'int' objects>, '__rfloordiv__': <slot wrapper '__rfloordiv__' of 'int' objects>, '__truediv__': <slot wrapper '__truediv__' of 'int' objects>, '__rtruediv__': <slot wrapper '__rtruediv__' of 'int' objects>, '__index__': <slot wrapper '__index__' of 'int' objects>, '__new__': <built-in method __new__ of type object at 0x7A2B48F0>, 'conjugate': <method 'conjugate' of 'int' objects>, 'bit_length': <method 'bit_length' of 'int' objects>, 'to_bytes': <method 'to_bytes' of 'int' objects>, 'from_bytes': <method 'from_bytes' of 'int' objects>, 'as_integer_ratio': <method 'as_integer_ratio' of 'int' objects>, '__trunc__': <method '__trunc__' of 'int' objects>, '__floor__': <method '__floor__' of 'int' objects>, '__ceil__': <method '__ceil__' of 'int' objects>, '__round__': <method '__round__' of 'int' objects>, '__getnewargs__': <method '__getnewargs__' of 'int' objects>, '__format__': <method '__format__' of 'int' objects>, '__sizeof__': <method '__sizeof__' of 'int' objects>, 'real': <attribute 'real' of 'int' objects>, 'imag': <attribute 'imag' of 'int' objects>, 'numerator': <attribute 'numerator' of 'int' objects>, 'denominator': <attribute 'denominator' of 'int' objects>, '__doc__': "int([x]) -> integer\nint(x, base=10) -> integer\n\nConvert a number or string to an integer, or return 0 if no arguments\nare given.  If x is a number, return x.__int__().  For floating point\nnumbers, this truncates towards zero.\n\nIf x is not a number or if base is given, then x must be a string,\nbytes, or bytearray instance representing an integer literal in the\ngiven base.  The literal can be preceded by '+' or '-' and be surrounded\nby whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.\nBase 0 means to interpret the base from the string as an integer literal.\n>>> int('0b100', base=0)\n4"}
  • ‌动态修改风险‌

直接操作 vars() 返回的字典可能破坏对象封装性,需谨慎使用。

2.5 举些实际场景中使用的栗子

  • 局部作用域用法,字符串格式化
name1 = "xiaoxu"
name2 = "xiaoli"
s = '{name1} and {name2} have happy life.'
print(s.format_map(vars()))

结果:

xiaoxu and xiaoli have happy life.
  • 根据实例对象__dict__属性字典执行序列化&反序列化
import jsonclass Apple:def __init__(self, name, price):self.name = nameself.price = pricedef __str__(self):return f"SApple(name={self.name}, price={self.price})"def __repr__(self):return "Apple{name=" +self.name + ",price=" + self.price + "}"apple = Apple("apple", 2.7)# vars用法
def serialize_instance(obj):d = {'__classname__': type(obj).__name__}d.update(vars(obj))return dclasses = {'Apple': Apple
}def unserialize_object(d):clsname = d.pop('__classname__', None)if clsname:cls = classes[clsname]obj = cls.__new__(cls)  # Make instance without calling __init__for key, value in d.items():setattr(obj, key, value)return objelse:return ds = json.dumps(apple, default=serialize_instance)
print(s)
print(type(s))
a = json.loads(s, object_hook=unserialize_object)
print(a)
print(vars(a))

结果:

{"__classname__": "Apple", "name": "apple", "price": 2.7}
<class 'str'>
SApple(name=apple, price=2.7)
{'name': 'apple', 'price': 2.7}
  • 动态对象配置与调试

通过vars() 直接操作对象的__dict__,动态添加、修改对象属性,并实现调试日志功能:

class DynamicConfig:def __init__(self, **kwargs):self.__dict__.update(kwargs)def add_property(self, key, value):vars(self)[key] = value  # 直接操作 __dict__def log_properties(self):print("Current properties:", vars(self))# 使用示例
config = DynamicConfig(name="Xiaoxu")
config.add_property("age", 30.0)
config.log_properties()  # 结果:Current properties: {'name': 'Xiaoxu', 'age': 30.0}# 动态删除属性
del vars(config)['age']
config.log_properties()  # 结果:Current properties: {'name': 'Xiaoxu'}
  • 模块级变量动态管理

动态操作模块变量并验证效果:

# 创建模块文件 my_module.py
var_a = 100
def func(): return "original"
from test import my_module# 修改模块变量
vars(my_module)['var_a'] = 200
vars(my_module)['new_var'] = "dynamic"
my_module.func = lambda: "modified"print(my_module.var_a)          # Output: 200
print(my_module.new_var)        # Output: dynamic
print(my_module.func())         # Output: modified
  • 上下文管理器监控局部变量

实现局部变量变化的自动记录,先看下面的栗子,分析为何达不到我们预期的效果:

class VariableMonitor:def __enter__(self):vcop = vars().copy()print("进入上下文:", vcop)print(id(vcop), id(vars()))vcop1 = vars().copy()print(id(vcop1))self.initial_vars = dict(vcop1)  # 记录进入时的局部变量return selfdef __exit__(self, exc_type, exc_val, exc_tb):final_vars = vars()print("结束上下文:", final_vars)changed = {k: final_vars[k] for k in final_vars if k not in self.initial_vars}print("Variables changed:", changed)# 使用示例
def process_data():name = "xiaoli"with VariableMonitor():x = 42y = [i for i in range(10)]process_data() 

我们预期是通过上下文管理器来实现with上下文的外部和内部局部变量的获取,但是执行结果却与我们的预期相反:

进入上下文: {'self': <__main__.VariableMonitor object at 0x01B6DB98>}
28755040 27812720
28757160
结束上下文: {'exc_type': None, 'exc_val': None, 'exc_tb': None, 'self': <__main__.VariableMonitor object at 0x01B6DB98>}
Variables changed: {'exc_type': None, 'exc_val': None, 'exc_tb': None}

上述的结果,是因为vars()方法获取的是局部命名空间,因此获取的是__enter__以及__exit__方法的局部命名空间参数。若想要同时获取with上下文的外部和内部局部变量,因with上下文的管理是在调用者栈帧process_data方法中触发的,所以可以使用inspect模块获取调用者的栈帧,从而访问外层上下文的局部变量,修改如下:

import inspectclass VariableMonitor:def __enter__(self):vcop = vars().copy()print("进入上下文:", vcop)print(id(vcop), id(vars()))vcop1 = vars().copy()print(id(vcop1))# 获取调用者(外层上下文)的栈帧self.frame = inspect.currentframe().f_back# 记录进入时的局部变量状态copy_f = self.frame.f_locals.copy()print("进入上下文时的栈帧局部变量:", copy_f)copy_f.update(vcop1)print("进入上下文时合并后的局部变量:", copy_f)self.initial_vars = copy_freturn selfdef __exit__(self, exc_type, exc_val, exc_tb):local_vas = vars().copy()# 获取退出时的局部变量状态final_vars = self.frame.f_localsprint("退出上下文时的栈帧局部变量:", final_vars)final_vars.update(local_vas)print("退出上下文时的合并局部变量:", final_vars)# 找出新增或修改的变量changed = {k: final_vars[k] for k in final_vars if k not in self.initial_vars}print("Variables changed:", changed)# 使用示例
def process_data():name = "xiaoli"with VariableMonitor():x = 42y = [i for i in range(10)]process_data()

执行结果符合预期:

进入上下文: {'self': <__main__.VariableMonitor object at 0x01ABDD90>}
27092184 27091824
32887624
进入上下文时的栈帧局部变量: {'name': 'xiaoli'}
进入上下文时合并后的局部变量: {'name': 'xiaoli', 'self': <__main__.VariableMonitor object at 0x01ABDD90>, 'vcop': {'self': <__main__.VariableMonitor object at 0x01ABDD90>}}
退出上下文时的栈帧局部变量: {'name': 'xiaoli', 'x': 42, 'y': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
退出上下文时的合并局部变量: {'name': 'xiaoli', 'x': 42, 'y': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'exc_type': None, 'exc_val': None, 'exc_tb': None, 'self': <__main__.VariableMonitor object at 0x01ABDD90>}
Variables changed: {'x': 42, 'y': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 'exc_type': None, 'exc_val': None, 'exc_tb': None}

上述的inspect.currentframe().f_back,inspect.currentframe()就是当前栈帧,也就是__enter__或者__exit__方法所在的栈帧,f_back意即它们的调用者栈帧,这里是process_data方法栈帧,因此可以获取到with上下文的外部、内部定义的局部变量了。

  • 复杂继承结构中的属性合并

在多层继承中动态合并父类属性:

class Base:base_var = "base"class Parent(Base):parent_var = "parent"class Child(Parent):def __init__(self):self.child_var = "child"def collect_vars(self):combined = {}# 遍历 MRO 继承链收集所有类的属性for cls in self.__class__.mro():if hasattr(cls, '__dict__'):combined.update(vars(cls))combined.update(vars(self))  # 合并实例属性combined = {k: v for k, v in combined.items() if not k.startswith("__")}return combinedobj = Child()
print(obj.collect_vars())

结果输出包含base_var、parent_var、child_var的合并字典:

{'collect_vars': <function Child.collect_vars at 0x011B8C40>, 'parent_var': 'parent', 'base_var': 'base', 'child_var': 'child'}

由此总结可知,vars() 的核心价值在于其动态性,适用于但不局限于如下各个方面:

  • 动态属性管理(配置注入、调试)
  • 元编程与类结构分析
  • 模块级变量操作
  • 上下文敏感的变量监控
  • 复杂对象结构的属性聚合

2.6 总结

vars() 是调试和动态编程的实用工具,通过字典形式直观展示对象属性或作用域变量。其核心应用包括属性分析、模块检查及动态编程,但需注意作用域差异和对象限制。

http://www.xdnf.cn/news/5307.html

相关文章:

  • 2025年渗透测试面试题总结-渗透测试红队面试四(题目+回答)
  • 免费 无需安装 批量图片压缩 高压缩比与画质保留软件
  • 【验证哥德巴赫猜想(奇数)】2021-11-19 15:54
  • ClassLoader类加载机制的核心引擎
  • C/C++复习--C语言中的函数详细
  • 强化学习系列:深度强化学习和DQN
  • 短剧平台流量突围!端原生片源授权成破局关键
  • 暗物质卯引力挂载技术
  • 【Bluedroid】蓝牙 HID 设备服务注册流程源码解析:从初始化到 SDP 记录构建
  • Docker基础入门
  • C++学习之模板初阶学习
  • 金丝雀/灰度/蓝绿发布的详解
  • 【免费工具】图吧工具箱2025.02正式版
  • 【比赛真题解析】篮球迷
  • 链表头插法的优化补充、尾插法完结!
  • 【数据结构与算法】——图(一)
  • anaconda部分基本指令
  • JavaWeb基础
  • Docker容器网络连接失败与镜像拉取异常全解析
  • 【RT-Thread Studio】nor flash配置Fal分区
  • “睿思 BI” 系统介绍
  • 2025年大模型RAG技术的实践总结
  • 2025-05-10-渗透测试:MS14-068漏洞利用、复现黄金票据(随笔)
  • 如何修改进程优先级?
  • 【漫话机器学习系列】250.异或函数(XOR Function)
  • Java游戏服务器开发流水账(4)游戏的数据持久化
  • Google Earth Pro(谷歌地球)2025大陆版安装教程
  • C# WinForm DataGridView 非常频繁地更新或重新绘制慢问题及解决
  • Docker、Docker-compose、K8s、Docker swarm之间的区别
  • 渠道销售简历模板范文