Python反射

ops/2024/9/22 23:36:45/

1、何为反射

1.1、概念

反射(Reflection)是计算机科学中的一个术语,指的是一种在运行时动态地获取、操作和修改一个语言的特定对象的能力。在编程中,反射可以让程序在运行时动态地获取类的信息,包括类的属性、方法和构造函数等,而不需要在编译时确定。

使用反射,程序可以在运行时动态地创建一个对象、调用对象的方法、访问对象的属性并修改它们的值、获取对象所属的类的信息等。这种动态性使得程序能够根据运行时的条件来决定如何处理对象,从而增加了程序的灵活性和可扩展性。

在许多编程语言中都提供了反射的机制,例如Ja、 Go、C#和Python等。具体的实现方式和使用方法可能会有所差异,但核心的概念和作用是相似的。

1.2、用途

以下是一些编程反射的常见用途:

  1. 动态实例化对象:通过反射可以根据类名字符串实例化对象,而不需要提前知道类的具体类型。这对于需要根据配置文件或用户输入来动态创建对象非常有用。

  2. 动态调用方法:通过反射可以在运行时动态调用类的方法,包括私有方法。这对于需要根据运行时条件选择不同的方法执行逻辑非常有用。

  3. 动态访问字段:通过反射可以在运行时动态获取和修改类的字段值,包括私有字段。这对于需要在运行时获取和修改对象的属性非常有用。

  4. 获取类的信息:通过反射可以获取类的各种元数据信息,如类名、父类、实现的接口、构造方法、方法、字段等。这对于开发一些通用的工具和框架非常有用。

  5. 注解处理:通过反射可以获取类、方法、字段上的注解信息,并动态根据注解做一些处理,如生成文档、验证参数等。

  6. 动态代理:通过反射可以创建动态代理对象,用于在不修改现有类的情况下增强类的行为。

总体而来,静态类型的编程语言,例如java反射API的能力要强于动态语言。

2、java反射案例

我们先来看看java版本的反射。假设我们有一个汽车类。该类的属性与方法都是私有的。正常是无法调用的。

java">public class Car {private static int output;private String brand;private int year;public Car() {}public Car(String brand, int year) {this.brand = brand;this.year = year;}private void drive() {System.out.println("Driving the car...");}public static int getOutput(){return output;}
}

使用反射,我们是可以实现动态创造类示例,调用私有方法,调用私有方法。 

java">import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class ReflectExample {public static void main(String[] args) {try {// 获取Class对象Class<?> carClass = Class.forName("Car");// 实例化对象Constructor<?> constructor = carClass.getConstructor(String.class, int.class);Object carObject = constructor.newInstance("BMW", 2024);// 调用私有方法Method driveMethod = carClass.getMethod("drive");driveMethod.setAccessible(true);driveMethod.invoke(carObject);// 访问私有字段Field brandField = carClass.getDeclaredField("brand");brandField.setAccessible(true);String brand = (String) brandField.get(carObject);System.out.println("Car brand: " + brand);} catch (Exception e) {e.printStackTrace();}}
}

我们也可以查看所有的属性及方法(包括静态以及示例)

java">  public static void main(String[] args) {Class<Car> clazz = Car.class;List<String> instanceFields = new ArrayList<>();List<String> staticFields = new ArrayList<>();List<String> instanceMethods = new ArrayList<>();List<String> staticMethods = new ArrayList<>();for (Field field : clazz.getDeclaredFields()) {if (Modifier.isStatic(field.getModifiers())) {staticFields.add(field.getName());} else {instanceFields.add(field.getName());}}for (Method method : clazz.getDeclaredMethods()) {if (Modifier.isStatic(method.getModifiers())) {staticMethods.add(method.getName());} else {instanceMethods.add(method.getName());}}System.out.println("所有静态属性");System.out.println(staticFields);   //[output]System.out.println("所有实例属性");  System.out.println(instanceFields); //[brand, year]System.out.println("所有静态方法");System.out.println(staticMethods);   //[getOutput]System.out.println("所有实例方法");System.out.println(instanceMethods); //[drive]}

3、Python反射

3.1、反射API

Python中的反射API主要集中在hasattrgetattrsetattr这三个内置函数,以及delattr函数等。

需要注意的是,API涉及属性与方法时,不区分属性和方法(虽然英文单词表示属性,python类内部属性与方法在同一个dict),而不像java那样,属性与方法区别对应。

hasattr(object, name)判断对象是否具有某个属性或方法
getattr(object, name)获取对象的某个属性或方法
setattr(object, name,value)设置对象的某个属性或方法
delattr(object, name)删除对象的某个属性或方法
dir(obj)返回对象obj的所有属性和方法的名称列表
type(obj):返回对象obj的类型
inspect模块提供了更高级的反射功能,可以获取对象的签名、参数、源代码等信息。

3.2、反射案例

class MyClass:def __init__(self):self.x = 10self.y = 20def my_method(self):print("Hello, reflection!")def my_method2(self, word):print("Hello!", word)obj = MyClass()# 使用getattr获取属性值
print(getattr(obj, 'x'))  # 输出: 10# 使用setattr设置属性值
setattr(obj, 'x', 20)
print(obj.x)  # 输出: 20# 使用hasattr检查属性是否存在
print(hasattr(obj, 'x'))  # 输出: True
print(hasattr(obj, 'y'))  # 输出: False# 使用dir获取对象的所有属性和方法 (屏蔽内置属性)
attrs = [x for x in dir(obj) if not x.startswith('__')]
print(attrs)# 使用type获取对象的类型
print(type(obj))  # 输出: <class '__main__.MyClass'># 使用inspect模块获取对象的信息
import inspect# 获取方法签名
method_sig = inspect.signature(obj.my_method2)
print(method_sig)  # 输出: (word)# 获取方法的参数名称
method_params = method_sig.parameters
print([param for param in method_params])  # 输出: ['word']# 获取方法的源代码
method_source = inspect.getsource(obj.my_method)
print(method_source)  # 输出: def my_method(self):\n    print("Hello, reflection!")

如果dir()方法的参数是一个class的话,返回所有实例方法、静态方法,静态属性;如果dir()方法的参数是一个对象的话,返回所有实例方法、静态方法,静态属性,还有加上所有实例属性(构造函数内部申明的属性)。

class MyClass:staticField = 100def __init__(self):self.x = 10self.y = 20def my_method(self):print("Hello, reflection!")@staticmethoddef my_method2(self, word):print("Hello!", word)obj = MyClass()
static_attrs = [x for x in dir(MyClass) if not x.startswith('__')]
print(static_attrs)# 使用dir获取对象的所有属性和方法 (屏蔽内置属性)
attrs = [x for x in dir(obj) if not x.startswith('__')]
print(attrs)

3.3、与java的比较

3.3.1、访问实例属性的区别

java作为一门静态语言,在编译阶段已经确定了类的结构(包括静态方法,实例方法,静态属性,实例属性,即使热更新也无法改变类的结构)。因此,java可以直接通过获取所有字段,见上一节java反射案例。

python只有通过dir()方法传入对象参数的时候,才可以获取所有的字段(因为python允许运行期动态增减字段)。

3.3.2、通过构造函数反射生成实例

java允许反射获取class的指定构造函数,并动态创建实例。比较常见的做法是,每个类提供一个无参构造函数,根据反射依次获取所有字段的类型,动态设置。

java">public class Main {public static void main(String[] args) throws Exception{Class<?> carClass = Class.forName("Car");// 指定有参构造函数Constructor<?> constructor = carClass.getConstructor(String.class, int.class);Object car1 = constructor.newInstance("BMW", 2024);// 无参构造函数Object car2 = Class.forName("Car").newInstance();}
}

python实现同样的功能,道路显得有点曲折。无参构造函数内部申明所有的实例属性,反射创建无状态实例,通过dir()方法获取所有字段,再通过setattr()动态设值。

class MyClass:def __init__(self, name):self.name = namedef say_hello(self):print(f"Hello, {self.name}!")# 要创建一个类的实例,可以使用以下代码:
class_name = "MyClass"
args = ("John",)# 使用globals()函数来获取全局命名空间中的类,并使用type()函数来创建实例对象
if class_name in globals():class_object = globals()[class_name]instance = type(class_name)(*args)instance.say_hello()  # 输出: Hello, John!
else:print(f"Class {class_name} not found")


http://www.ppmy.cn/ops/30101.html

相关文章

第12章 软件测试基础(第一部分)概念、质量保证、测试用例、测试执行过程

一、软件测试 &#xff08;一&#xff09;定义 动态验证计算机程序对有限的测试用例集是否可产生期望的结果的过程。测试计划是描述了要进行的测试活动的范围、方法、资源和进度的文档。编写测试计划目的&#xff1a;使测试工作顺利进行、使项目参与人员沟通更舒畅、使测试工…

MATLAB 代数

MATLAB 代数 到目前为止&#xff0c;我们已经看到所有示例都可以在MATLAB及其GNU&#xff08;也称为Octave&#xff09;中运行。但是&#xff0c;为了求解基本的代数方程&#xff0c;MATLAB和Octave几乎没有什么不同&#xff0c;因此我们将尝试在单独的部分中介绍MATLAB和Octa…

表情识别 | LBP+SVM实现脸部动态特征的人脸表情识别程序(Matlab)

表情识别 | LBPSVM实现脸部动态特征的人脸表情识别程序&#xff08;Matlab&#xff09; 目录 表情识别 | LBPSVM实现脸部动态特征的人脸表情识别程序&#xff08;Matlab&#xff09;预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1 运行环境 程序运行在Windows系统下&am…

软件测试期末试题

一、选择题 一、单项选择题&#xff1a; 可以作为软件测试结束的标志&#xff08;B &#xff09;。 A.使用了特定的测试用例 B.错误强度曲线下降到预定的水平 C.查出了预定数目的错误 D.按照测试计划中所规定的时间进行了测试 软件测试用户和设计交换最频繁的方法&#…

实现优先队列——C++

目录 1.优先队列的类模板 2.仿函数的讲解 3.成员变量 4.构造函数 5。判空&#xff0c;返回size&#xff0c;返回队头 6.插入 7.删除 1.优先队列的类模板 我们先通过模板来进行初步了解 由上图可知&#xff0c;我们的模板里有三个参数&#xff0c;第一个参数自然就是你要存储的数…

React Context

Context https://juejin.cn/post/7244838033454727227?searchId202404012120436CD549D66BBD6C542177 context 提供了一个无需为每层组件手动添加 props, 就能在组件树间进行数据传递的方法 React 中数据通过 props 属性自上而下(由父及子)进行传递&#xff0c;但此种用法对…

区块链 | IPFS:Merkle DAG(进阶版)

&#x1f98a;原文&#xff1a;Merkle DAGs: Structuring Data for the Distributed Web &#x1f98a;写在前面&#xff1a;本文属于搬运博客&#xff0c;自己留存学习。 1 Merkle DAG 当我们在计算机上表示图时&#xff0c;必须通过提供节点和边的具体表示来编码我们的数据…

【通信中间件】Fdbus HelloWorld实例

Fdbus实例教程 Fdbus简介 Fdbus 全称 Fast Distributed Bus&#xff08;高速分布式总线&#xff09;&#xff0c;提供IPCRPC功能。适用于多种OS&#xff1a; LinuxQNXAnroidOSWindow Fdbus本质是Socket&#xff0c;IPC基于Unix domain socket&#xff0c;RPC基于TCP。使用G…