ZetCode

Python setattr 函数

上次修改时间:2025 年 4 月 11 日

这篇综合指南探讨了 Python 的 setattr 函数,该函数可以动态地设置对象的属性。我们将介绍基本用法、动态对象操作以及属性赋值的实际例子。

基本定义

setattr 函数将一个值分配给对象的属性。它接受三个参数:对象、属性名称(字符串)和要分配的值。

主要特征:适用于任何 Python 对象,允许动态属性赋值,并且等同于 obj.attr = value 但更灵活。

基本属性赋值

这是一个简单的用法示例,展示了 setattr 如何将属性分配给对象,这与直接赋值等效,但属性名称是动态的。

basic_setattr.py
class Person:
    pass

p = Person()

# Equivalent to p.name = 'John'
setattr(p, 'name', 'John')

# Equivalent to p.age = 30
setattr(p, 'age', 30)

print(p.name)  # John
print(p.age)   # 30

此示例演示了基本的 setattr 用法。我们创建一个简单的 Person 类并动态添加属性。函数调用等同于直接属性赋值。

主要的区别在于,setattr 允许在运行时确定属性名称,而直接赋值需要在编写时知道名称。

动态属性创建

当属性名称是动态的或来自外部源时,setattr 就会大放异彩。此示例显示了从字典动态创建属性。

dynamic_attrs.py
class Settings:
    pass

config = {
    'theme': 'dark',
    'font_size': 14,
    'auto_save': True
}

settings = Settings()

for key, value in config.items():
    setattr(settings, key, value)

print(settings.theme)      # dark
print(settings.font_size)  # 14
print(settings.auto_save) # True

此示例从配置字典中动态地在 Settings 对象上创建属性。属性名称和值来自字典。

当加载配置或将数据结构转换为具有属性访问权限的对象时,此模式非常有用。它比硬编码属性更灵活。

使用内置类型

setattr 还可以修改内置类型实例,尽管某些内置类型是不可变的。此示例展示了如何使用可变对象。

builtin_types.py
# Works with custom classes
class Box:
    pass

b = Box()
setattr(b, 'contents', 'books')
print(b.contents)  # books

# Works with some built-in types
d = {}
setattr(d, 'description', 'storage dict')
print(d.description)  # storage dict

# Won't work with immutable types like int
try:
    x = 5
    setattr(x, 'description', 'number')
except AttributeError as e:
    print(f"Error: {e}")  # can't set attributes of built-in/extension type 'int'

此示例显示了 setattr 如何与自定义类和一些内置的可变类型一起使用。但是,像整数这样的不可变类型不支持动态属性赋值。

该错误表明 Python 阻止修改未设计用于动态属性赋值的内置类型。

动态对象初始化

此示例演示了如何在 __init__ 中使用 setattr,以根据输入参数动态初始化对象属性。

dynamic_init.py
class DynamicObject:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
            
    def __repr__(self):
        attrs = ', '.join(f"{k}={v}" for k, v in self.__dict__.items())
        return f"DynamicObject({attrs})"

obj = DynamicObject(name='Alice', age=25, city='London')
print(obj)       # DynamicObject(name=Alice, age=25, city=London)
print(obj.name)  # Alice
print(obj.age)   # 25

DynamicObject 类接受任意关键字参数,并使用 setattr 将它们转换为属性。 这创建了一个灵活的对象,该对象可以具有在创建时指定的任何属性。

当创建数据容器或在编写类时不知道确切属性时,此模式非常有用。 它类似于 JavaScript 对象的工作方式。

属性和描述符交互

这个高级示例展示了 setattr 如何与属性和描述符交互,尊重 Python 的属性访问协议。

properties.py
class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
    
    @property
    def celsius(self):
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        if value < -273.15:
            raise ValueError("Temperature below absolute zero")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32

temp = Temperature(25)

# Uses the property setter
setattr(temp, 'celsius', 30)
print(temp.celsius)  # 30

# Can't set read-only property
try:
    setattr(temp, 'fahrenheit', 100)
except AttributeError as e:
    print(f"Error: {e}")  # can't set attribute

# Respects property validation
try:
    setattr(temp, 'celsius', -300)
except ValueError as e:
    print(f"Error: {e}")  # Temperature below absolute zero

此示例演示了 setattr 可以正确地与 Python 的属性系统交互。 它在可用时调用属性设置器,并尊重只读属性。

celsius 设置器中的验证由 setattr 触发,表明它遵循与直接赋值相同的属性访问规则。

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直撰写编程文章。 迄今为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我在编程教学方面拥有超过十年的经验。

列出所有 Python 教程