Python反射

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、反射案例

python 复制代码
class MyClass:
    def __init__(self):
        self.x = 10
        self.y = 20

    def 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()方法的参数是一个对象的话,返回所有实例方法、静态方法,静态属性,还有加上所有实例属性(构造函数内部申明的属性)。

python 复制代码
class MyClass:
    staticField = 100
    def __init__(self):
        self.x = 10
        self.y = 20

    def my_method(self):
        print("Hello, reflection!")

    @staticmethod
    def 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()动态设值。

python 复制代码
class MyClass:
    def __init__(self, name):
        self.name = name
    
    def 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")

 
相关推荐
Theodore_10221 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
网易独家音乐人Mike Zhou2 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
安静读书2 小时前
Python解析视频FPS(帧率)、分辨率信息
python·opencv·音视频
冰帝海岸2 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象3 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了3 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring
小二·4 小时前
java基础面试题笔记(基础篇)
java·笔记·python
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
懒洋洋大魔王4 小时前
RocketMQ的使⽤
java·rocketmq·java-rocketmq
武子康4 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud