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")

 
相关推荐
一头生产的驴14 分钟前
java整合itext pdf实现自定义PDF文件格式导出
java·spring boot·pdf·itextpdf
YuTaoShao21 分钟前
【LeetCode 热题 100】73. 矩阵置零——(解法二)空间复杂度 O(1)
java·算法·leetcode·矩阵
zzywxc78724 分钟前
AI 正在深度重构软件开发的底层逻辑和全生命周期,从技术演进、流程重构和未来趋势三个维度进行系统性分析
java·大数据·开发语言·人工智能·spring
3gying41 分钟前
chromedriver
python
DES 仿真实践家2 小时前
【Day 11-N22】Python类(3)——Python的继承性、多继承、方法重写
开发语言·笔记·python
YuTaoShao3 小时前
【LeetCode 热题 100】56. 合并区间——排序+遍历
java·算法·leetcode·职场和发展
程序员张33 小时前
SpringBoot计时一次请求耗时
java·spring boot·后端
llwszx6 小时前
深入理解Java锁原理(一):偏向锁的设计原理与性能优化
java·spring··偏向锁
云泽野6 小时前
【Java|集合类】list遍历的6种方式
java·python·list
二进制person7 小时前
Java SE--方法的使用
java·开发语言·算法