跳转至

面向对象编程

面向对象编程的四个特性:封装、抽象、继承、多态

封装 Encapsulation

封装就是将数据(属性)和代码(方法)绑定到单一的单位(类)中,并限制对某些组件的直接访问

限制访问

  • 私有的

__ 开头的实例属性或方法,只能由类本身访问,不能被外部类或子类访问

注意:方法中的变量前使用 __ 是无效的,只对类或实例属性生效

会触发名称改编(name mangling)的过程,比如 __name 会被改编为 _ClassName__name

也就是说,只不过换了个名称,其实还是可以访问到的,但是不推荐

  • 受保护的

_ 开头的属性或方法被视为受保护的,只应在类本身及其子类中使用,不过这只是一种约定俗成,实际还是可以访问到的

class MyClass:
    """
    每个定义体开头的三个双引号字符串都会被当作自我描述信息
    可被 __doc__ 以及 help() 等获取
    """

    # 类属性,当类被加载时即生效
    # 类属性会被所有实例共享,无论公有的还是私有的
    __private_class_var = "私有类属性"
    _class_var = "受保护的类属性"
    class_var = "正常类属性"


    """
    初始化方法,通常被称作构造器,可选
    类被实例化时自动执行,主要目的是在对象创建时初始化其属性或执行其他必要的设置工作
    至少要有一个self参数,self代表的就是实例化后的实例(类似其它语言中的this)
    只能默认返回None,而不能return其它值
    """
    def __init__(self):
        # 实例属性,每个实例都有独立的副本
        self.__private_var = "I am private"
        self._protected_var = "I am protected"


    # 私有方法不会被import导入
    def __private_method(self):
        return "This is a private method"


    def _protected_method(self):
        return "This is a protected method"


    # 实例方法,当需要处理实例的具体状态或行为时使用(即 self 指向的对象)
    # 实例化后调用,需要参数,惯用 self
    def get_value(self, value):
        return self.value


    # 类方法,当需要处理类级别的数据或行为,或者当需要创建类实例的替代方法时使用
    # 需要参数,惯用 cls
    @classmethod
    def get_count(cls):
        return cls.count


    # 静态方法,当方法与类有关但不需要访问类或实例的任何属性或方法时使用,通常是为了代码的组织性
    # 无需传递 cls 或 self 参数
    # 实例方法调用静态方法:self.xxx 或者 <className>.xxx
    @staticmethod
    def method_static(*args):
        return *args


obj = MyClass()

# 访问私有变量
print(obj.__private_var)  # 会抛出 AttributeError
print(obj._MyClass__private_var)  # 非标准访问是可以的


# 全局变量
__global_private = "Global private"  # 不会变为私有的,这样写没意义
_global_protected = "Global protected"  # 但可以是受保护的


def function():
    # 方法前的双下划线没有特殊影响,这样写没意义
    __local_in_function = "Local in function"

内置方法

虽然在日常使用中,人们经常将 __init__ 称为构造函数,但从技术上讲,真正的构造函数是 __new__

__init__ 更准确地说是初始化函数。这种称呼混淆主要是因为在大多数情况下,对象的创建和初始化是在 __new____init__ 中协作完成的,而大部分开发者只需要关注 __init__

class MyClass:
    # 构造函数,属于静态方法
    """
    负责创建一个类的实例。这个方法是在对象的内存分配之后调用,但在其初始化之前执行。
    它是在类层次上操作的,也就是说,它与类本身而非实例相关联。
    """
    def __new__(cls, *args, **kwargs):
        print("Creating instance...")
        # 接收类本身作为第一个参数(通常命名为 cls),后面跟着其他参数,这些参数将传递给 __init__ 方法
        instance = super(MyClass, cls).__new__(cls)
        # 必须返回一个类的实例(通常是通过调用 object.__new__(cls) 实现)
        return instance


    # 初始化函数,属于实例方法
    """
    用于初始化新创建的对象。它在 __new__ 创建了一个实例之后被调用,用来给这个新创建的对象设置初始状态、添加属性等。
    """
    def __init__(self, name):
        print("Initializing instance...")
        self.name = name


    # 析构方法,内存中被释放(垃圾回收)时自动执行
    # 无须自定义,但也可以在这里定义一些释放时要做的事
    def __del__(self):
        print("Python自带内存分配和释放机制")

一些其它方法

# obj()带括号时执行
def __call__(self, *args, **kwargs):
    print('可以用callable(Foo)函数判断是否可执行')

# print(obj)时执行
# 如果没定义则返回:<__main__.Foo object at 0x00E90DB0>
def __str__(self):
    return '打印实例时返回的信息'

# 通常__repr__与__str__返回值一样,为了调试用的
__repr__ = __str__

调用

# 如果文件作为脚本执行时
if __name__ == "__main__":
    # 访问类属性
    print(Foo.x_class)
    print(Foo._Foo__x)  # 私有

    Foo.method_class()  # 调用类方法
    Foo.method_static()  # 调用静态方法

    obj = Foo()  # 自动执行 __init__函数
    obj()  # 自动执行 __call__函数
    print(obj)  # 自动执行__str__函数

    # 访问实例属性,无法访问不加 self. 的属性
    print(obj.a)
    print(obj._Foo__b)  # 私有

    obj.func()  # 调用实例方法
  • 获取成员(即类的属性和方法)
    # 列出类中的所有成员
    print(Foo.__dict__)
    """
    {
    '__module__': '__main__',

    'x_class': '类变量',
    '_Foo__x': '私有类变量'
    '__doc__': '注释',

    '__init__': <function Foo.__init__ at 0x009394F8>,
    '__dict__': <attribute '__dict__' of 'Foo' objects>,
    '__weakref__': <attribute '__weakref__' of 'Foo' objects>,

    'func': <function Foo.func at 0x00939588>,
    }
    """

    # 列出对象中的所有成员
    print(obj.__dict__)
    """
    {'a': 'a', '_Foo__b': 'b'}
    """

    # 返回类/模块的描述类信息
    print(Foo.__doc__)

    # 返回当前操作的对象在属于哪个模块
    print(obj.__module__)
    """
    __main__
    """

    # 返回当前操作的对象属于哪个类
    print(obj.__class__)
    """
    <class '__main__.Foo'>
    """

抽象 Abstraction

抽象是隐藏复杂性只暴露必要功能的过程。

抽象类不能被实例化,只能被其他类继承。

抽象方法定义在抽象类中,必须在继承抽象类的子类中实现。

from abc import ABC, abstractmethod

class Vehicle(ABC):
    @abstractmethod
    def move(self):
        pass

class Car(Vehicle):
    def move(self):
        print("The car is moving")

多态 Polymorphism

多态是指相同的函数或方法在不同的对象上可以有不同的实现。在 Python中,它是通过简单地定义具有相同名称的多个方法,或者通过重写继承的方法来实现的。

class Animal:
    def speak(self):
        pass

class Dog(Animal):
    def speak(self):
        print("Bark")

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

def animal_sound(animal):
    animal.speak()  # 调用实际传入对象的speak方法

dog = Dog()
cat = Cat()

animal_sound(dog)  # 输出 Bark
animal_sound(cat)  # 输出 Meow

继承 Inheritance

"""
Python 中一切皆对象,全部继承自 object 基类(也称作父类、超类)
object 在 Py2.x 中必须写,在 Py3.x 可省略不写
"""
# class Parent(object)  # py2
class Parent:  # py3
    def __init__(self, a, b):
        self.a = a
        self.b = b


# 如果当前类没有定义__init__,则自动使用父类的构造器
class Child(Parent):
    pass


# 如果当前类定义了__init__,也可以使用super()方法调用父类的构造器
class Child(Parent):
    def __init__(self, a, b):
        super().__init__(a, b)

    # 调用父类方法,Py2.x 必须这样写,super(子类名, self/父类名).方法名()
    super().add(x, y)  # Py3.x可以简写


# 实例化子类时记得传入父类__init__()所需的变量
child_instance = Child(1, 2)
  • 多重继承

多重继承的方法解析顺序遵循称为C3线性化或C3超类线性化的算法,然后得到的类及其父类的顺序列表被称作 MRO(Method Resolution Order),Python 通过这个顺序来解析一个类的属性和方法调用

class A:
    pass

class B(A):
    pass

class C(A):
    pass

class D(B, C):
    pass


"""
1. 先从左到右,B > C,所以先调用 B
2. 然后调用 B 的父类 A
3. 然后调用 C 以及 C 的父类
4. 由于 A 是 B 和 C 的共同父类,所以 A 要在 C 后被调用

则最终MRO顺序为:D -> B -> C -> A -> object
"""


# 查看调用顺序,也可以用 D.__mro__
D.mro()  # [<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>

混入类

如果一个类的作用是为多个不相关的子类提供方法,实现复用但不体现“是什么”的关系,应该把这个类明确地定义为混入类。

Python 中没有声明混入类的正式方式,只是约定俗成在名称中加入 Mixin 后缀

特点

  • 单一功能:通常只提供一组相关的功能,而不是代表一个实体。
  • 无独立实例:绝不应被实例化,只应被多重继承,即一个具体类不应该只继承混入类。
  • 无状态:通常不存储状态,只提供方法,即避免定义__init__()以及属性。
# 提供日志记录功能
class LoggerMixin:
    def log(self, message):
        """Simple method to log messages"""
        print(f"[LOG] {message}")

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

    def work(self):
        print(f"{self.name} is working.")

# Using LoggerMixin to add logging functionality to Worker
class LoggedWorker(Worker, LoggerMixin):
    def __init__(self, name):
        super().__init__(name)

    def work(self):
        self.log(f"{self.name} started working.")
        super().work()
        self.log(f"{self.name} finished working.")

# Example usage
worker = LoggedWorker("Alice")
worker.work()