Python 反射
Python 反射
2024年5月21日
摘要
Python的反射是指程序在运行时能够检查和操作自身结构的能力。它允许代码动态地基于字符串或其他元数据来执行任务,比如导入模块、查找和调用函数、访问或修改对象属性。反射常用于实现动态行为,比如插件系统、动态加载类、根据配置执行特定方法等,提高了代码的灵活性和可扩展性。简单来说,反射就是让代码能够自我观察和自我调整。本文将对其进行介绍。
属性操作
假设有一个类:
class Example:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} is {self.age} years old."
def __repr__(self):
return f"Example('{self.name}', {self.age})"
def is_adult(self):
return self.age >= 18
我们创建一个此类的对象:
example = Example("Tim Cook", 50)
检查属性
我们可以使用 hasattr
函数检查一个类中是否存在一个属性,此属性包含变量,方法,和内置属性:
print(hasattr(example, "name"))
# True
print(hasattr(example, "is_adult"))
# True
print(hasattr(example, "__str__"))
# True
print(hasattr(example, "study"))
# False
其将返回一个 Bool
值。
读写属性
常规属性
我们可以使用 setattr
函数为某一个对象更新一个已经存在的属性或是创建一个新的属性,用 getatt
来读取一个属性。
可以直接操作一个已经存在的属性:
setattr(example, "name", "Jane")
print(getattr(example, "name"))
# Jane
以上操作等价于:
example.name = "Jane"
print(example.name)
# Jane
也可以创建一个原本不存在的属性:
setattr(example, "father", "Sam")
print(getattr(example, "father"))
# Sam
同样的,也等价于:
example.father = "Sam"
print(example.father)
# Sam
但当我们直接去访问一个并不存在的属性时:
print(getattr(example, "mother"))
# AttributeError: 'Example' object has no attribute 'mother'
会得到一个报错。和直接尝试获取一样:
print(example.mother)
# AttributeError: 'Example' object has no attribute 'mother'
函数属性
除了普通的属性外,我们也可以使用 gerattr
去读取一个函数:
print(getattr(example, "is_adult"))
# <bound method Example.is_adult of Example('Tim Cook', 50)>
等价于:
print(example.is_adult)
# <bound method Example.is_adult of Example('Tim Cook', 50)>
或是调用这个函数:
print(getattr(example, "is_adult")())
# True
等价于:
print(example.is_adult())
# True
当然,也可以使用 setattr
去创建一个函数:
setattr(example, "eat", lambda x: f'"{x} is delicious"')
然后获取这个函数:
print(getattr(example, "eat"))
# <function <lambda> at 0x104f22020>
print(example.eat)
<# function <lambda> at 0x104f22020>
或是调用他:
print(getattr(example, "eat")("apple"))
# "apple is delicious"
print(example.eat("apple"))
# "apple is delicious"
模块导入
Python 的反射机制也可以允许我们动态的导入某个模块,但要先导入模块 importlib
:
import importlib
此后,便可以动态的进行以下形式的导入:
importlib.import_module('torch')
等价于:
import torch
但是使用反射时,并不支持模块重命名获部分导入,例如:
import numpy as np
from torch import Tensor
from matplotlib import pyplt as plt
均不支持。
总结
使用反射机制可以在运行时再决定一些代码执行的方式。使用 if-else
和 caee
能够描述的分支数量总归是有限的,而使用反射则可根据字符串输入产生无限可能的分支,在某些场景下会很有用,但需要注意安全和效率问题。
License:
CC BY 4.0