Python中的类操作

8 阅读16分钟

在Python中,类(class)是面向对象编程(OOP)的核心概念之一。类提供了一种将数据和功能组织在一起的方式,使得代码更加模块化、可重用和易于维护。 类操作的相关示例代码如下:

# 基本结构
"""
核心概念:封装、继承、多态。
类的结构:类属性、实例属性、方法(实例方法/类方法/静态方法)。
访问控制:通过命名约定(_ 和 __)实现封装。
特殊方法:重载运算符或定义对象行为(如 __str__)。
高级特性:抽象基类、属性装饰器、组合设计模式。
"""


class Dog:
    """类的文档字符串(描述类的功能)"""
    # 定义了一个类属性species,它被所有Dog类的实例共享。
    species = "Canis familiaris"  # 类属性(所有实例共享)

    # __init__方法:这是一个特殊的方法,也称为构造函数。当创建Dog类的实例时,__init__方法会自动被调用。
    # self是一个约定俗成的参数名,它代表类的实例本身。
    # 在调用__init__方法时,Python 会自动将实例作为第一个参数传递给self。name和age是用户传递的参数,用于初始化实例的属性。
    def __init__(self, name, age):  # __init__ 是构造函数,用于初始化实例属性。
        """初始化方法(构造函数)"""
        self.name = name  # 实例属性
        self.age = age

    # self参数:同样,self参数代表类的实例本身,通过self.name可以访问实例的name属性。
    def bark(self):  # self 表示实例自身,是实例方法的第一个参数。
        """实例方法"""
        print(f"bark:{self.name} says: Woof!")


# 实例化对象
# 创建了一个Dog类的实例my_dog,并将名字"Buddy"和年龄3传递给__init__方法进行初始化。
my_dog = Dog("Buddy", 3)
my_dog.bark()  # 输出: Buddy says: Woof!
print(my_dog.species)  # 输出: Canis familiaris


# 类定义示例
class EricClass:
    # 类属性
    class_attribute = "I am a class attribute"

    # 初始化方法(构造器)
    def __init__(self, instance_attribute):
        self.instance_attribute = instance_attribute

    # 实例方法
    def instance_method(self):
        return f"Instance attribute: {self.instance_attribute}"


# 类属性与实例属性
# 类属性:定义在类体中的变量,属于类本身,所有实例共享同一个类属性。
# 实例属性:通过__init__方法或其他实例方法定义的变量,属于类的实例(对象),每个实例有自己的实例属性。
class BarryClass:
    class_attribute = "Class Attribute"

    def __init__(self, instance_value):
        self.instance_attribute = instance_value


# 创建实例
obj1 = BarryClass("Instance 1")
obj2 = BarryClass("Instance 2")

# 访问类属性
print(BarryClass.class_attribute)  # 输出: Class Attribute
print(obj1.class_attribute)  # 输出: Class Attribute

# 修改类属性
BarryClass.class_attribute = "Modified Class Attribute"
print(obj1.class_attribute)  # 输出: Modified Class Attribute
print(obj2.class_attribute)  # 输出: Modified Class Attribute

# 访问实例属性
print(obj1.instance_attribute)  # 输出: Instance 1
print(obj2.instance_attribute)  # 输出: Instance 2


# 方法
# 实例方法:第一个参数是self,代表类的实例本身。
# 类方法:使用@classmethod装饰器,第一个参数是cls,代表类本身。
# 静态方法:使用@staticmethod装饰器,不接收self或cls参数。
class JimClass:
    class_attribute = "Class Attribute"

    def __init__(self, instance_value):
        self.instance_attribute = instance_value

    # 实例方法
    def instance_method(self):
        return f"Instance attribute: {self.instance_attribute}"

    # 类方法
    @classmethod
    def class_method(cls):
        return f"Class attribute: {cls.class_attribute}"

    # 静态方法
    @staticmethod
    def static_method():
        return "This is a static method"


# 创建实例
obj = JimClass("Instance Value")

# 调用方法
print(obj.instance_method())  # 输出: Instance attribute: Instance Value
print(obj.class_method())  # 输出: Class attribute: Class Attribute
print(JimClass.class_method())  # 输出: Class attribute: Class Attribute
print(obj.static_method())  # 输出: This is a static method
print(JimClass.static_method())  # 输出: This is a static method


# 继承
class ParentClass:
    def __init__(self, parent_value):
        self.parent_attribute = parent_value

    def parent_method(self):
        return f"Parent attribute: {self.parent_attribute}"


class ChildClass(ParentClass):
    def __init__(self, parent_value, child_value):
        super().__init__(parent_value)  # 调用父类的构造器
        self.child_attribute = child_value

    def child_method(self):
        return f"Child attribute: {self.child_attribute}"


# 创建子类实例
child_obj = ChildClass("Parent Value", "Child Value")

# 调用父类方法
print(child_obj.parent_method())  # 输出: Parent attribute: Parent Value

# 调用子类方法
print(child_obj.child_method())  # 输出: Child attribute: Child Value


# 继承与多态
class Animal:
    def __init__(self, name):
        self.name = name

    # speak 方法被定义为抛出 NotImplementedError 异常。
    # 这是一种抽象方法的实现方式,表明这个方法在基类中没有具体实现,子类必须重写这个方法才能正常使用。
    def speak(self):
        raise NotImplementedError("子类必须实现此方法")


# class Cat(Animal) 表明 Cat 类继承自 Animal 类。这意味着 Cat 类可以继承 Animal 类的所有属性和方法。
# 子类通过 class SubClass(ParentClass) 语法继承父类。
# Python 支持多重继承(多个父类),需注意方法解析顺序(MRO)。
class Cat(Animal):  # 继承自 Animal
    # 子类可重写父类方法(如 speak())实现多态。
    def speak(self):
        print(f"{self.name} says: Meow!")


class Duck(Animal):
    def speak(self):
        print(f"{self.name} says: Quack!")


# 多态
# 多态:多态是面向对象编程的一个重要特性,它允许不同的对象对同一消息做出不同的响应。
# 在这个例子中,animals 列表包含了 Cat 和 Duck 类的实例。
# 当使用 for 循环遍历这个列表并调用每个对象的 speak 方法时,Python 会根据对象的实际类型调用相应的 speak 方法。
# 这就是多态的体现,相同的方法调用(animal.speak())在不同的对象上产生了不同的行为。
animals = [Cat("Kitty"), Duck("Donald")]
for animal in animals:
    animal.speak()  # 分别输出 Kitty says: Meow! 和 Donald says: Quack!


# 封装
class EncapsulatedClass:
    def __init__(self, value):
        self.__private_attribute = value  # 私有属性

    def get_private_attribute(self):
        return self.__private_attribute

    def set_private_attribute(self, value):
        self.__private_attribute = value


# 创建实例
obj = EncapsulatedClass("Private Value")

# 访问私有属性(通过公共方法)
print(obj.get_private_attribute())  # 输出: Private Value

# 修改私有属性(通过公共方法)
obj.set_private_attribute("New Private Value")
print(obj.get_private_attribute())  # 输出: New Private Value

# 直接访问私有属性(不推荐,会导致AttributeError)
# print(obj.__private_attribute)  # AttributeError: 'EncapsulatedClass' object has no attribute '__private_attribute'


# 封装与访问
# 通过双下划线 __var 定义私有属性/方法(实际会被重命名为 _ClassName__var)。
# 单下划线 _var 表示“受保护”成员(约定俗成,非强制)。
# 通过公共方法(如 get_balance())提供安全访问。
class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # 私有属性(双下划线开头)

    # 在 Python 中,不支持方法重载,即不能定义多个同名的方法。当你定义了两个 __init__ 方法时,后面的定义会覆盖前面的定义。
    # def __init__(self, balance, name):
    #     self.__balance = balance  # 私有属性(双下划线开头)
    #     self.__name = name

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount

    def get_balance(self):  # 公共方法访问私有属性
        return self.__balance


account = BankAccount(1000)
# account.__balance  # 报错:AttributeError(无法直接访问)
# 通过调用公共方法 get_balance 来访问私有属性 __balance,避免直接访问私有属性导致的 AttributeError 异常。
print(account.get_balance())  # 输出: 1000


class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    # __add__ 方法:这是 Python 中的特殊方法,用于重载加法运算符 +。
    # 当对两个 Vector 实例使用 + 运算符时,Python 会自动调用 __add__ 方法。
    def __add__(self, other):  # 重载加法运算符
        # 该方法将两个向量的对应分量相加,然后创建并返回一个新的 Vector 实例,其 x 和 y 分量分别为相加后的结果。
        return Vector(self.x + other.x, self.y + other.y)

    # __str__ 方法:这也是 Python 中的特殊方法,用于定义对象的字符串表示形式。
    # 当使用 print() 函数打印 Vector 实例时,Python 会自动调用 __str__ 方法,并将其返回值作为输出内容。
    def __str__(self):  # 定义 print() 时的输出
        return f"Vector({self.x}, {self.y})"


v1 = Vector(2, 3)
v2 = Vector(4, 5)
v3 = v1 + v2
print(v3)  # 输出: Vector(6, 8)
"""
常见魔术方法:
__init__: 构造函数
__str__: 用户友好字符串表示(print(obj))
__repr__: 解释器友好字符串表示
__len__: 定义 len(obj) 的行为
__eq__: 重载 == 运算符
__getitem__/__setitem__: 实现索引访问(如 obj[key])
"""


# 定义一个基类 Shape
class Shape:
    def area(self):
        # 抛出 NotImplementedError 异常,提示子类需要实现该方法
        raise NotImplementedError("子类必须实现 area 方法")


# 定义一个矩形类,继承自 Shape 类
class Rectangle(Shape):
    def __init__(self, length, width):
        # 初始化矩形的长和宽
        self.length = length
        self.width = width

    def area(self):
        # 计算矩形的面积
        return self.length * self.width


# 定义一个圆形类,继承自 Shape 类
class Circle(Shape):
    def __init__(self, radius):
        # 初始化圆的半径
        self.radius = radius

    def area(self):
        import math
        # 计算圆的面积
        return math.pi * self.radius ** 2


# 定义一个三角形类,继承自 Shape 类
class Triangle(Shape):
    def __init__(self, base, height):
        # 初始化三角形的底和高
        self.base = base
        self.height = height

    def area(self):
        # 计算三角形的面积
        return 0.5 * self.base * self.height


# 多态函数,用于计算不同形状的面积
def calculate_area(shape):
    # 调用传入形状对象的 area 方法
    return shape.area()


# 创建不同形状的对象
rect = Rectangle(5, 10)
circle = Circle(3)
triangle = Triangle(4, 6)

# 将不同形状的对象存储在列表中
shapes = [rect, circle, triangle]

# 遍历列表,调用 calculate_area 函数计算每个形状的面积并打印结果
for shape in shapes:
    # type(shape) 返回 shape 对象的类型,__name__ 属性是该类型的名称,用于获取形状对象的类型名,例如 Circle、Rectangle 等。
    print(f"{type(shape).__name__} 的面积是: {calculate_area(shape)}")

# 类方法与静态方法
"""
类方法(@classmethod)接收 cls 参数,可修改类状态。
静态方法(@staticmethod)无特殊参数,用于工具函数,与类相关但无需访问实例或类属性。
"""


# 定义一个类,包含一个类属性 class_attr,一个类方法 update_class_attr 用于更新类属性的值。
# 类方法和静态方法是 Python 中类里两种特殊类型的方法.
# 在定义方式上,类方法和静态方法的定义主要区别在于装饰器和第一个参数。
# 在调用方式上,类方法和静态方法都可以通过类名直接调用,不过类方法还能通过实例调用。
# 在对类的访问权限上,类方法可以访问和修改类的属性和调用其他类方法,而静态方法不能直接访问类或实例的属性和方法。
# 在继承场景下,类方法和静态方法表现不同。在子类中调用类方法时,cls 参数会指向子类,因此可以实现基于子类的行为。静态方法在继承时不会因为子类的不同而有不同的行为,它只是简单地被继承,不会考虑类的具体类型。
# 综上所述,类方法更注重与类本身的交互,而静态方法更像是独立于类的普通函数,只是在逻辑上与类相关联。

class MyClass:
    # 类属性是属于类本身的属性,所有类的实例都可以共享该属性。
    class_attr = 0
    class_attribute = 10

    # @classmethod是一个装饰器,用于将 update_class_attr 方法标记为类方法。
    # 类方法的第一个参数通常命名为 cls,它指向类本身,而不是类的实例。
    @classmethod
    # 方法接受两个参数:cls 表示类本身,value 是要更新类属性的值。
    def update_class_attr(cls, value):  # cls 指向类本身
        cls.class_attr = value

    @classmethod
    def eric_class_method(cls):
        # 访问类属性
        print(f"Class attribute value: {cls.class_attribute}")
        # 修改类属性
        cls.class_attribute = 20

    # @staticmethod是一个装饰器,用于将 utility_method 方法标记为静态方法。
    # 静态方法不依赖于类或类的实例,因此不需要 self 或 cls 参数。
    @staticmethod
    def utility_method(a, b):  # 无 self/cls 参数
        # 下面这行代码会报错,不能直接访问类属性
        # print(cls.class_attribute) 或 print(class_attribute)
        return a + b

    # 在 Python 里,如果一个函数(或者方法)没有显式地使用 return 语句返回一个值,那么它默认会返回 None。
    def eric_instance_method(self):
        print("这是一个实例方法")
        # 这里没有 return 语句,默认返回 None


# 通过类名直接调用类方法 update_class_attr,并传入参数 5。这会将类属性 class_attr 的值更新为 5。
# 类方法和静态方法都可以通过类名直接调用,也可以通过实例调用。
MyClass.update_class_attr(5)  # 通过类名调用
print(MyClass.class_attr)  # 输出: 5
MyClass().update_class_attr(6)  # 通过类实例调用
print(f"call by class instance:{MyClass.class_attr}")  # 输出:call by class instance:6

MyClass.eric_class_method()  # 输出:Class attribute value: 10
print(MyClass.class_attribute)  # 输出 20

print(MyClass.class_attr)  # 输出: 6
print(MyClass.utility_method(2, 3))  # 通过类名调用,输出: 5
print(MyClass().utility_method(3, 4))  # 通过实例调用,输出:7

# 使用 print 函数来输出 MyClass().eric_instance_method() 的返回值。
# 由于 eric_instance_method 方法返回 None,所以 print 函数就会输出 None。
print(MyClass().eric_instance_method())  # 输出:这是一个实例方法 \n None
# 如果不想让 print 函数输出 None,可以直接调用方法而不使用 print 函数
MyClass().eric_instance_method()  # 输出:这是一个实例方法
# 下面的一行代码有错误,实例方法不能通过类名调用。
# MyClass.eric_instance_method()
# 执行上面一行代码,会出现:TypeError: MyClass.eric_instance_method() missing 1 required positional argument: 'self'

# 属性装饰器(@property)
"""
@property 将方法转换为属性,可定义 getter/setter 控制属性访问。
用于数据验证、动态计算属性(如 area)或创建只读属性。
"""


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

    # @property:这是 Python 的一个装饰器,用于将 radius 方法转换为一个只读属性。
    # 使用 @property 装饰器后,可以像访问普通属性一样访问 radius,而不需要显式调用方法。
    @property
    def radius(self):  # Getter
        return self._radius

    @radius.setter
    def radius(self, value):  # Setter
        if value >= 0:
            self._radius = value
        else:
            raise ValueError("半径不能为负")

    @property
    def area(self):  # 只读属性
        return 3.14 * self._radius ** 2


c = Circle(5)
print(f"get by original radius:{c.area}")  # get by original radius:78.5
c.radius = 10  # 调用 setter(验证逻辑)
print(c.area)  # 输出: 314.0(调用 getter)

# 抽象基类(ABC)
"""
通过 abc 模块定义抽象基类(强制子类实现特定方法)。
抽象类不能直接实例化,用于定义接口规范。
"""

# abc 模块是 Python 中用于创建抽象基类的标准库模块。
# ABC 是一个基类,用于创建抽象基类,所有的抽象基类都应该继承自 ABC。
# abstractmethod 是一个装饰器,用于将方法标记为抽象方法。
from abc import ABC, abstractmethod


# 定义了一个名为 Shape 的抽象基类,它继承自 ABC。
class Shape(ABC):
    # @abstractmethod:这是一个装饰器,将 area 方法标记为抽象方法。
    # 抽象方法是一种没有具体实现的方法,它只是定义了方法的签名(名称和参数),具体实现需要在子类中完成。
    @abstractmethod
    # 定义了一个抽象方法 area,该方法没有具体实现,只有一个 pass 语句作为占位符。
    def area(self):
        pass


# 定义了一个名为 Rectangle 的具体类,它继承自抽象类 Shape。
# 由于继承了抽象类,Rectangle 类必须实现抽象类中定义的所有抽象方法,否则它也会被视为抽象类,无法实例化。
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):  # 必须实现抽象方法
        return self.width * self.height


# 抽象类不能被实例化,因为它包含抽象方法,而抽象方法没有具体实现。如果尝试实例化抽象类 Shape,会抛出 TypeError 异常。
# shape = Shape()  # 报错:无法实例化抽象类
rect = Rectangle(3, 4)
print(rect.area())  # 输出: 12

# 在抽象类中添加一个抽象属性
# 在 Python 中,如果要在抽象类里添加抽象属性,可以借助 @property 装饰器和 @abstractmethod 装饰器结合使用
"""
实现步骤如下:
导入必要的模块:使用 abc 模块中的 ABC 和 abstractmethod 来定义抽象基类和抽象方法。
定义抽象类:让类继承自 ABC,以此表明它是一个抽象基类。
定义抽象属性:利用 @property 装饰器把方法转化为属性,再用 @abstractmethod 装饰器将其标记为抽象方法,这样该属性就成为抽象属性了。
定义具体子类:子类必须实现抽象类中的所有抽象属性和抽象方法,不然子类也会被视为抽象类,无法实例化。
"""
"""
抽象属性和抽象方法都是 Python 中抽象基类(ABC)的重要概念,它们都用于定义抽象基类的接口,要求子类必须实现,
但在使用方式、用途和语法上存在一些区别:
语法定义上的区别:
抽象属性通过结合 @property 和 @abstractmethod 装饰器来定义,本质上是将一个方法转换为属性的形式。
抽象方法使用 @abstractmethod 装饰器直接定义,是一个普通的方法签名。
调用方式的区别:
抽象属性像访问普通属性一样使用点号(.)来访问,不需要使用括号调用。
抽象方法需要像调用普通方法一样使用括号来调用,并可以传递必要的参数。
用途侧重点的区别:
抽象属性更侧重于表示对象的某种状态或特征,通常用于提供对象的只读或可读写的属性值,强调数据的获取和封装。
抽象方法更侧重于表示对象的某种行为或操作,通常用于定义对象可以执行的动作,强调行为的实现和多态性。
状态维护上的区别:
抽象属性通常与对象的状态相关联,它可能会依赖于对象的其他属性来计算或获取值。可以用于封装对象的内部状态,使得外部代码可以以统一的方式访问对象的属性。
抽象方法主要用于定义对象的行为,它不直接与对象的状态相关联,而是关注对象可以执行的操作。方法可以接受参数并返回结果,用于实现复杂的业务逻辑。
"""

from abc import ABC, abstractmethod


class Shape1(ABC):  # 定义抽象类
    # 定义抽象属性
    # @property 和 @abstractmethod 装饰器组合使用,将 area 方法定义为抽象属性。
    # 这意味着所有继承自 Shape1 的子类都必须实现 area 属性。
    @property
    @abstractmethod
    def area(self):
        pass

    # 定义抽象方法
    # @abstractmethod 装饰器把 perimeter 方法标记为抽象方法,子类也必须实现该方法。
    @abstractmethod
    def perimeter(self):
        pass


class Rectangle1(Shape1):  # 定义具体子类
    def __init__(self, width, height):
        self.width = width
        self.height = height

    # 实现抽象属性
    # @property 装饰器将 area 方法转化为属性,实现了抽象类中定义的抽象属性 area。
    @property
    def area(self):
        return self.width * self.height

    # 实现抽象方法
    def perimeter(self):
        return 2 * (self.width + self.height)


# 创建 Rectangle1 类的实例
rect = Rectangle1(3, 4)
# 访问抽象属性
print(rect.area)  # 直接访问属性,不需要括号, 输出: 12
# 调用抽象方法
print(rect.perimeter())  # 需要使用括号调用方法, 输出: 14

# 组合与委托
"""
组合:将其他类的实例作为属性(“有一个”关系)。
继承:表示“是一个”关系。
优先使用组合而非继承,提高代码灵活性。
组合是一种 “has - a”(有一个)的关系,即 Car 类有一个 Engine 对象;而继承是一种 “is - a”(是一个)的关系。
"""


class Engine:
    def start(self):
        print("引擎启动")


class Car:
    def __init__(self):
        # self.engine = Engine() 这行代码体现了组合的概念。
        # 在 Car 类的实例化过程中,创建了一个 Engine 类的实例,并将其赋值给 Car 类实例的 engine 属性。
        # 也就是说,每一个 Car 对象都 “拥有” 一个 Engine 对象。
        self.engine = Engine()  # 组合(而非继承)

    #  定义了 Car 类的 start 方法。
    def start(self):
        # self.engine.start() 这行代码实现了方法的委托。
        # 当调用 Car 对象的 start 方法时,实际上是调用了其内部 Engine 对象的 start 方法,从而实现了汽车启动的功能。
        self.engine.start()  # 委托给 Engine 对象


car = Car()
car.start()  # 输出: 引擎启动