25.Python基础篇-面向对象

一、面向对象编程思想

面向对象编程(OOP) 是一种编程范式,将现实世界的事物建模为对象,通过对象之间的交互实现功能。

OOP 的四大基本特性:

封装(Encapsulation):将数据和操作封装在类中,对外隐藏实现细节。

继承(Inheritance):子类继承父类的属性和方法,实现代码复用。

多态(Polymorphism):不同类的对象可以以统一接口调用,表现出不同行为。

抽象(Abstraction):对对象的复杂部分进行隐藏,仅暴露必要接口。

二、类与对象

定义一个类

class Person:
    def __init__(self, name, age):  # 构造方法
        self.name = name  # 实例变量
        self.age = age

    def greet(self):  # 实例方法
        print(f"Hello, my name is {self.name} and I am {self.age} years old.")

 

创建对象

p1 = Person("Alice", 25)
p1.greet()  # 输出: Hello, my name is Alice and I am 25 years old.

组合的概念

组合就是一个类的属性,是另一个类的对象(实例)

组合的特点:

  • 强调“有一个”关系:对象通过包含其他对象来实现功能,例如“汽车有一个引擎”。
  • 松耦合:组合比继承更加灵活,能减少类之间的耦合。
  • 优先于继承:通常在子类并不需要完全继承父类行为时,推荐使用组合

演示同一个功能,使用继承和组合两种不同方式的实现:

# 使用继承实现
class Engine:
    def start(self):
        print("Engine started")

class Car(Engine):  # Car 是一个 Engine
    def drive(self):
        print("Car is driving")

car = Car()
car.start()  # 输出: Engine started
car.drive()  # 输出: Car is driving

 

# 使用组合实现
class Engine:
    def start(self):
        print("Engine started")

class Car:
    def __init__(self):
        self.engine = Engine()  # Car 有一个 Engine

    def drive(self):
        self.engine.start()
        print("Car is driving")

car = Car()
car.drive()
# 输出:
# Engine started
# Car is driving

 

三、类的基本组成

属性

类属性

属于类本身,是所有对象都能共享的。

可以通过类调用,也可以通过对象调用。按照标准,类属性使用类调用。

class MyClass:
    class_variable = "类属性"

# 创建对象
obj = MyClass()

# 通过对象访问类属性
print(obj.class_variable)  # 输出: 类属性

# 通过类访问类属性
print(MyClass.class_variable)  # 输出: 类属性

注意事项:

通过对象修改类属性时,实际上会在对象的实力属性中创建一个新的同名属性,而不会真正修改类属性。有修改类属性的需求,应使用类名调用进行修改。

class MyClass:
    class_variable = "类属性"

# 创建对象
obj = MyClass()

obj.class_variable = "尝试修改类属性"  # 在对象上创建了同名实例属性
print(obj.class_variable)  # 输出: 尝试修改类属性
print(MyClass.class_variable)  # 输出: 类属性(类属性未改变)

实例属性

属于具体对象,各实例独立的

class Person:
    species = "Homo sapiens"  # 类属性

    def __init__(self, name, age):
        self.name = name  # 实例属性
        self.age = age


zs = Person("张三", 18)
ls = Person("李四", 19)
print(zs.name)  # zs.name为zs这个对象的name属性
# 输出:张三
print(ls.name)  # ls.name为ls这个对象的name属性。二者相互独立
# 输出:李四

print(zs.species)  # zs的species和ls的为同一个值,并且直接用类调也是同样的,因为类属性可以共享
# 输出:Homo sapiens
print(ls.species)
# 输出:Homo sapiens
print(Person.species)
# 输出:Homo sapiens

 

方法

实例方法

第一个参数是 self,用于操作实例属性

只能使用对象调用

作用

  • 表示当前对象self 用于访问当前实例的属性和方法。
  • 区分局部变量和实例变量:通过 self 明确指定是属于对象的变量,而不是方法中的局部变量。
  • 动态绑定属性:允许每个对象拥有自己的独立属性。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):  # 实例方法
        print("这是实例方法")

p = Person("Alice", 25)
p.greet()  # 通过对象调用实例方法
# 输出:这是实例方法

 

类方法

1.使用 @classmethod 装饰,第一个参数是 cls,操作类属性。

2.cls表示当前类。

  2.1cls的作用:

   cls 可以访问类的方法和属性

   在继承关系中,使用cls可以动态指向子类,确保在类方法中调用的是子类的行为。

3.可以通过对象调用,也可以通过类调用。但是按照规范应使用类调用。

class MyClass:
    class_variable = "I am a class variable"

    @classmethod
    def class_method(cls):
        return f"Accessing: {cls.class_variable}"

# 调用
print(MyClass.class_method())  # 输出: Accessing: I am a class variable

obj = MyClass()
print(obj.class_method())  # 输出: Accessing: I am a class variable

4.典型应用场景

class Counter:
    count = 0  # 类属性

    @classmethod
    def increment(cls):
        cls.count += 1

# 调用
Counter.increment()
Counter.increment()
print(Counter.count)  # 输出: 2

 

静态方法

1.使用 @staticmethod 装饰,不需要 selfcls 参数。

2.具有特征:既不操作实例属性,也不操作类属性。

3.在完全的面向对象编程语言中用的多,因为每个方法都必须写在类中。Python因为也可以面向函数编程,所以并不是一定需要。

4.应用场景:

 当前静态方法与当前类并没有什么引用关系,只是放在这里个类中而已。

 比如:工具类。一个工具类中有很多通用的方法,这些方法之间没有关联关系,且并不引用类中的内容。

class Calculator:
    @staticmethod
    def add(a, b):  # 静态方法
        return a + b

print(Calculator.add(5, 10))  # 输出: 15

三种方法的对比总结:

适用于不同的场景

实例方法

  • 定义实例行为,如获取或修改实例属性。

类方法

  • 创建工厂方法(如通过不同参数构造类的实例)。
  • 处理全局状态共享。

静态方法

  • 定义工具函数,逻辑与类有关但不需要类的上下文(如数学运算)。

四、内置方法

__init__():初始化方法,也叫类的构造方法

每次创建类的对象时,__init__() 都会自动调用。不是必须要写的。init方法需要的参数,就是实例化一个类的时候需要传的参数

class Person:
    def __init__(self, name, age):
        # 第一个参数必须是self,表示当前实例(对象)。其他参数可以自定义
        self.name = name  # 将参数值赋给实例属性
        self.age = age

# 创建对象时会调用 __init__()
p1 = Person("Alice", 25)
p2 = Person("Bob", 30)

print(p1.name, p1.age)  # 输出: Alice 25
print(p2.name, p2.age)  # 输出: Bob 30

与普通方法的区别:

  • 调用时机__init__() 在创建对象时自动调用,普通方法需要通过实例调用。
  • 用途__init__() 通常用于初始化实例属性,而普通方法用于实现实例的其他功能。

注意事项:

  • __init__() 不是构造对象的方法:实际创建对象的过程是由 __new__() 方法完成的,__init__() 只是用于初始化对象。
  • 不要返回值__init__() 不能返回值,否则会引发错误。

 __repr__

使用 repr() 或者在解释器中输入对象时,__repr__方法会被自动调用。

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"Dog('{self.name}', {self.age})"

dog = Dog('Buddy', 3)

#使用repr方法时会自动调用__repr__方法
print(repr(dog))  # 输出:Dog('Buddy', 3)

__str__

当print()或者str()打印对象时,__str__方法会被调用。

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name} is {self.age} years old."

dog = Dog('Buddy', 3)
print(str(dog))  # 输出:Buddy is 3 years old.

__str__与__repr__两个方法的关系

当使用print或str方法打印一个对象时,会优先调用__str__,如果当前对象没有实现__str__,则会调用__repr__。

但是当使用repr()时,对象没有实现__repr__会调用父类的__repr__,并不会再调用__str__。

class A:
    def __repr__(self):
        return "__repr__"

a = A()
print(a)  # __repr__:没有str会调用__repr__
print(str(a))  # __repr__
print(repr(a))  # __repr__


class A:
    def __str__(self):
        return "__str__"

a = A()
print(repr(a))  # <__main__.A object at 0x1047a9310>

__len__

调用内建函数 len() 时自动调用的方法

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __len__(self):
        return len(self.name)

dog = Dog('Buddy', 3)
print(len(dog))  # 输出:5,返回名字的长度

__new__

__new__是一个创建对象的特殊方法,它在__init__方法之前调用。__new__负责创建对象的实例,而__init__则负责初始化对象。__new__通常在涉及单例模式或自定义对象创建行为时使用。

# __new__方法的应用场景:单例
# __new__方法确保只有一个Singleton实例被创建,实现了单例模式。
class Singleton:
    _instance = None

    def __new__(cls):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

s1 = Singleton()
s2 = Singleton()
print(s1 is s2)  # 输出:True,两个对象是同一个实例

__del__

析构函数,当对象被垃圾回收时自动调用。通常用于释放对象占用的外部资源,如文件句柄、网络连接等。

当使用del关键字删除时,会先自动调用对象的__del__方法,再从内存中删除对象。

class Dog:
    def __init__(self, name):
        self.name = name
        print(f"{self.name} is created!")

    def __del__(self):
        print(f"{self.name} is destroyed!")

dog = Dog('Buddy')
del dog  # 输出:Buddy is destroyed!

__call__

得对象变得可调用。换句话说,如果我们定义了__call__,我们可以像调用函数一样调用该对象。

class Dog:
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print(f"{self.name} is called!")

dog = Dog("Buddy")
# 在对象后面加上括号,像调用函数一样写时,会自动执行该对象的__call__方法
dog()  # 输出:Buddy is called!

tem系列

__getitem__

允许通过方括号 [] 的形式获取对象的指定项,类似于访问列表或字典的元素。

__setitem__

允许通过方括号 [] 的形式为对象的指定项赋值。

__delitem__

允许通过方括号 [] 的形式删除对象的指定项。

代码演示

class Foo:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __getitem__(self, item):
        if hasattr(self,item):
            return self.__dict__[item]

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __delitem__(self, key):
        del self.__dict__[key]

f = Foo("zs", 30, "")
# 使用[]调用getitem,以访问对象的属性
print(f['name'])  # 输出:zs
# 使用[]调用setitem,给对象添加一个属性
f['hobby'] = ""
print(f.hobby)  # 输出:男
print(f.__dict__)  # 输出:{'name': 'zs', 'age': 30, 'sex': '男', 'hobby': '男'}
# 调用delitem方法
del f["hobby"]
print(f.__dict__)  # 输出:{'name': 'zs', 'age': 30, 'sex': '男'}

__eq__

在使用 == 比较两个对象时的行为。也就是说当使用==时会自动调用左边对象的__eq__方法。根据__eq__方法实现的自定义相等性来判断两个对象是否相等

代码演示:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        if isinstance(other, Person):
            return self.name == other.name and self.age == other.age
        return False

p1 = Person('Alice', 30)
p2 = Person('Alice', 30)
p3 = Person('Bob', 25)

print(p1 == p2)  # 输出:True
print(p1 == p3)  # 输出:False

 

__hash__

返回对象的哈希值

如果实现了__eq__,且对象需要用于哈希集合(如set或字典的键),通常也需要实现__hash__方法

代码演示:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        if isinstance(other, Person):
            return self.name == other.name and self.age == other.age
        return False

    def __hash__(self):
        return hash((self.name, self.age))

p1 = Person('Alice', 30)
p2 = Person('Alice', 30)
p3 = Person('Bob', 25)

people = {p1, p2, p3}
print(len(people))  # 输出:2,因为p1和p2被认为是相等的

五、继承

继承的基本概念

  • 继承:子类继承父类的属性和方法,代码可复用。
  • 父类(基类):被继承的类。
  • 子类(派生类):继承父类的类,可以添加或重写父类的方法。

Python中的继承介绍:

1.Python3中,所有的类都是新式类。只有在新式类中才可以使用super关键字。

2.并且Python3中所有的类都继承自object类。

3.在继承中如果子类和父类共同拥有一个属性或者方法,则优先使用子类。子类没有则用父类。

单继承

super关键字:用于调用父类的方法,是继承中非常常用的关键字。

class Parent:
    def __init__(self, name):
        self.name = name

class Child(Parent):
    def __init__(self, name, age):
        super().__init__(name)  # 调用父类构造方法
        self.age = age

child = Child("Alice", 10)
print(child.name)  # 输出:Alice
print(child.age)   # 输出:10

 

单继承代码演示

# 定义父类
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f"{self.name} says Woof!"


# 定义子类
class Dog(Animal):  # 括号内为要继承的父类
    pass


class Cat(Animal):  # 括号内为要继承的父类
    pass


# 测试
dog = Dog("Buddy")
cat = Cat("Kitty")
print(dog.speak())  # 输出:Buddy says Woof!
print(cat.speak())  # 输出:Kitty says Meow!

方法的重写(Override)

子类可以重新定义父类的方法,覆盖父类的方法实现。

class Parent:
    def greet(self):
        return "Hello from Parent"

class Child(Parent):
    def greet(self):
        return "Hello from Child"

child = Child()
print(child.greet())  # 输出:Hello from Child

 

多继承

多继承说明

多继承是指一个类继承多个父类

C.__mro__

查看类的继承顺序(方法解析顺序)。

代码演示:

class A:
    def greet(self):
        return "Hello from A"

class B:
    def greet(self):
        return "Hello from B"

class C(A, B):  # 多继承
    pass

c = C()
print(c.greet())  # 输出:Hello from A (根据继承顺序)

print(C.__mro__)  # 输出:(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)

# 继承顺序为,优先继承C,C没有再去A找。最后直到object类

多继承中的继承顺序

   广度优先规则 python3和python2中的新式类使用

   深度优先规则 python2中的经典类使用

多继承中使用super关键字显式调用指定父类:

class D(B, C):  # D继承自B和C
    def __init__(self):
        super(B, self).__init__()  # 显式调用B的__init__方法
        print("D's __init__")
    
    def speak(self):
        super(C, self).speak()  # 显式调用C的speak方法

# 测试
d = D()
d.speak()  # 显式调用C的speak

 

六、接口类与抽象类

接口类

介绍:接口类用于定义一组方法签名(即方法的声明),这些方法没有实现。接口类强调“行为”而不是“实现”,用于为不同的类提供统一的接口规范。

Python中的接口类:

Python没有内置的interface关键字,但可以通过**抽象基类(ABC)**来模拟接口的行为。所有方法都使用@abstractmethod标记,并且不能有方法实现。

用法代码演示:

from abc import ABC, abstractmethod

class Animal(ABC):  # 定义一个接口类
    @abstractmethod
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    pass

# 不能在Python中定义一个真正的接口类,但可以通过这种方式来模拟接口行为
dog = Dog()
print(dog.speak())  # 输出:Woof!

# Cat类中没有实现speak方法,运行是会报错
cat = Cat()
print(cat.speak())  # 报错:TypeError: Can't instantiate abstract class Cat with abstract method speak

 

特征:

  没有实现:接口类的所有方法没有实现,只定义方法签名。

  提供统一的行为规范:所有实现接口的类必须实现接口中的方法。

  灵活性高:多个类可以实现同一个接口,不同类有不同的实现方式。

抽象类

介绍:抽象类是不能实例化的类,它提供了对子类的行为约定。抽象类可以包含已实现的方法(带有实现的函数)和抽象方法(仅定义,没有实现)。抽象类用于定义一些公共的行为,让子类继承并具体实现。

Python中的抽象类:

在Python中,抽象类通过abc模块来实现,使用ABC类来定义抽象类,并通过@abstractmethod装饰器定义抽象方法。

用法代码演示:

from abc import ABC, abstractmethod

class Animal(ABC):  # 定义一个抽象类Animal,继承ABC
    def __init__(self, name):
        self.name = name
    
    @abstractmethod  # 抽象方法,子类必须实现
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        return f"{self.name} says Woof!"

# animal = Animal("Generic")  # 错误:不能实例化抽象类
dog = Dog("Buddy")
print(dog.speak())  # 输出:Buddy says Woof!

 

特征:

  可以包含具体实现:例如Animal类中没有实现的speak方法是抽象方法,而构造函数是已实现的方法。

  子类必须实现抽象方法Dog类实现了抽象方法speak

  不能实例化:你不能直接实例化抽象类,必须通过继承来创建子类实例。

接口类与抽象类的区别

 七、多态

多态就是“一个接口,多种实现”。在Python中,多态的实现主要依赖于方法的重写和鸭子类型。

Python天生是支持多态的。

多态的代码演示:

class Animal:
    def speak(self):
        raise NotImplementedError("Subclasses should implement this!")

class Dog(Animal):
    def speak(self):
        return "Woof!"

class Cat(Animal):
    def speak(self):
        return "Meow!"

# 多态示例
animals = [Dog(), Cat()]
for animal in animals:
    print(animal.speak())


#输出:
#Woof!
#Meow!

 

鸭子类型

鸭子类型强调的是对象的行为而不是其类型。只要一个对象实现了所需的方法或属性,就可以被认为是一个合适的类型。

换句话说,Python不关心一个对象是哪个类的实例,只要这个对象拥有你想要调用的接口(方法或属性),就可以使用它

class Dog:
    def speak(self):
        print("Woof!")

class Cat:
    def speak(self):
        print("Meow!")

class Duck:
    def speak(self):
        print("Quack!")

def make_sound(animal):
    animal.speak()  # 不关心animal的具体类型,只关心它是否有speak方法

# 测试
make_sound(Dog())  # 输出:Woof!
make_sound(Cat())  # 输出:Meow!
make_sound(Duck()) # 输出:Quack!

在上面的代码中,make_sound函数并不关心传入的对象是DogCat,还是Duck,只要它们有一个speak方法,就可以调用。这就是鸭子类型的体现:只要对象具有某些特定的方法或行为,就可以当作所需的类型使用。

鸭子类型的优势:

  灵活性:你不需要显式检查对象的类型,只要它符合行为规范,就可以使用它。这使得Python的代码更加简洁和灵活。

  解耦:鸭子类型避免了对对象类型的强依赖,使得代码可以更加松耦合。这有助于提高代码的可扩展性。

  适应性强:无论一个类是如何定义的,只要实现了所需的方法,它就能在某个上下文中使用。比如,你可以随时让一个新类实现duck类型的方法,而不需要修改其他已有代码。

八、封装

封装的概念介绍

将对象的状态(属性)和行为(方法)包装在一起,对外提供访问接口,同时隐藏内部实现细节。封装通过访问控制的方式,保护数据不被直接修改,确保数据的安全性和一致性。

封装的目的

  隐藏实现细节:将复杂的实现细节隐藏在对象内部,只暴露简洁的接口。

  提高代码的安全性:通过限制外部对数据的访问和修改,防止不当的操作。

  提高可维护性:更容易更改和扩展内部实现,而不影响外部代码。

私有属性

受保护属性:使用一个下划线标记,外部不应该访问,但是Python没有强制禁止。

私有属性:属性名前面使用两个下划线“__”标记,标记之后外部不能直接访问

私有方法

受保护方法:使用一个下划线标记,外部不应该访问,但是Python没有强制禁止。

方法名前面使用两个下划线“__”标记,标记之后外部不能直接访问

class Car:
    def __init__(self, make, model):
        self.make = make        # 公共属性
        self._model = model     # 受保护属性
        self.__year = 2020      # 私有属性

    def get_year(self):
        return self.__year      # 提供对私有属性的访问接口

    def set_year(self, year):
        if year > 1885:         # 检查年份是否合法
            self.__year = year
        else:
            print("Invalid year!")

car = Car("Toyota", "Corolla")

print(car.make)       # 输出:Toyota  (公开属性)
print(car._model)     # 输出:Corolla (受保护属性,虽然可以访问,但不推荐)
# print(car.__year)    # 会抛出错误:AttributeError: 'Car' object has no attribute '__year'

# 正确访问私有属性
print(car.get_year())  # 输出:2020

car.set_year(2025)     # 设置新的年份
print(car.get_year())  # 输出:2025

@property装饰器

介绍:可以将一个方法转换为属性,提供了控制属性访问的能力,同时允许通过方法来动态计算属性值。

代码演示:

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property  # 通过 @property 提供了只读访问,外部只能读取radius但不能直接修改。
    def radius(self):
        return self._radius

    @radius.setter  # radius.setter 提供了对 radius 属性的写访问,通过实现这个方法,可以实现修改radius
    def radius(self, value):
        if value > 0:
            self._radius = value
        else:
            print("Radius must be positive!")

    @property
    def area(self):
        return 3.14 * self._radius ** 2

circle = Circle(5)
print(circle.radius)  # 5
print(circle.area)    # 78.5

circle.radius = 10    # 设置新的半径
print(circle.area)    # 314.0

circle.radius = -3    # 错误的半径值

 九、反射

反射的概念:反射可以在运行时获取对象的属性和方法;可以动态调用方法或访问属性;动态修改对象的属性值。

Python中的反射:Python没有显式的反射机制,但它提供了通过内置函数和方法来实现类似反射的功能,常见的包括:

getattr()

setattr()

hasattr()

delattr()

getattr()

函数用于获取对象的属性。如果该属性不存在,可以指定一个默认值。

getattr(object, attribute_name[, default])

  object:要获取属性的对象。

  attribute_name:属性的名称,可以是字符串。

 通过字符串获取变量:

class Dog:
    def __init__(self, name):
        self.name = name

dog = Dog("Buddy")

# 获取name属性
print(getattr(dog, "name"))  # 输出:Buddy

# 如果属性不存在,返回默认值
print(getattr(dog, "age", 3))  # 输出:3

setattr()

用于设置对象的属性。如果该属性不存在,会创建新属性

setattr(object, attribute_name, value)

  • object:要设置属性的对象。
  • attribute_name:属性名称。
  • value:设置的值。
class Dog:
    def __init__(self, name):
        self.name = name

dog = Dog("Buddy")

# 修改name属性
setattr(dog, "name", "Charlie")
print(dog.name)  # 输出:Charlie

# 动态设置新的属性
setattr(dog, "age", 5)
print(dog.age)  # 输出:5

 

hasattr()

用于检查对象是否具有指定的属性。

hasattr(object, attribute_name)

  • object:要检查的对象。
  • attribute_name:要检查的属性名称。
class Dog:
    def __init__(self, name):
        self.name = name

dog = Dog("Buddy")

# 检查是否具有name属性
print(hasattr(dog, "name"))  # 输出:True

# 检查是否具有age属性
print(hasattr(dog, "age"))   # 输出:False

 

可以在getattr方法使用前,先用hasattr检查要获取的变量是否存在。

delattr()

用于删除对象的属性。 

delattr(object, attribute_name)

  • object:要删除属性的对象。
  • attribute_name:要删除的属性名称。
class Dog:
    def __init__(self, name):
        self.name = name

dog = Dog("Buddy")

# 删除name属性
delattr(dog, "name")
print(hasattr(dog, "name"))  # 输出:False

 

除了动态操作属性之外,还可以动态调用方法

class Dog:
    def __init__(self, name):
        self.name = name

    def speak(self):
        print(f"{self.name} says woof!")

dog = Dog("Buddy")

# 动态获取并调用speak方法
method = getattr(dog, "speak")
method()  # 输出:Buddy says woof!

 

反射的应用场景

  • 动态方法调用:可以根据字符串动态调用类中的方法,常见于插件系统、框架设计等。
  • 对象序列化与反序列化:可以根据对象的属性动态地进行序列化或反序列化。
  • API调用:在API开发中,可以根据请求动态调用相应的方法。
  • 测试与调试:可以通过反射检查对象的状态、方法或属性,方便进行单元测试和调试。

 反射的优缺点

优点:

  • 灵活性:可以动态地访问和操作对象的属性、方法,增强了程序的灵活性。
  • 简化代码:能够通过少量代码实现复杂的动态功能,避免手动编写大量的条件判断和硬编码。
  • 适用于插件和框架开发:反射在插件化架构、框架开发等场景中非常有用,可以根据需求动态加载和调用功能模块。

缺点:

  • 降低代码可读性:反射通过动态操作对象的属性和方法,可能导致代码不易理解,增加了代码的复杂性。
  • 运行时错误:由于反射是基于字符串操作的,运行时可能出现属性或方法不存在的错误,增加了出错的风险。
  • 性能开销:反射会增加一定的性能开销,尤其是在高频调用的情况下。

 反射总结

  • 反射是Python的一项强大特性,可以在运行时动态地访问、修改对象的属性和方法。
  • Python的反射通过getattr()setattr()hasattr()delattr()等函数实现,允许在代码执行过程中动态获取和操作对象的行为。
  • 反射使得程序更加灵活、可扩展,但也可能导致代码的可读性下降,增加出错的可能性。
  • 在实际应用中,反射常用于框架设计、插件系统、动态API调用等场景。

 

posted @ 2024-12-10 01:33  邵杠杠  阅读(4)  评论(0编辑  收藏  举报