彭某的技术折腾笔记

彭某的技术折腾笔记

Python 反射

2024-05-21

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-elsecaee 能够描述的分支数量总归是有限的,而使用反射则可根据字符串输入产生无限可能的分支,在某些场景下会很有用,但需要注意安全和效率问题。

  • 0