面向对象编程#

面向对象编程(OOP)是一种编程范式,使用类和对象来组织代码。Python 完全支持面向对象编程。

类和对象基础#

定义类#

# 基本语法
class ClassName:
    """类的文档字符串"""
    # 类体

# 示例
class Dog:
    """狗类"""
    pass

# 创建对象(实例化)
my_dog = Dog()
print(type(my_dog))  # <class '__main__.Dog'>

实例属性#

class Dog:
    def __init__(self, name, age):
        """初始化方法"""
        self.name = name  # 实例属性
        self.age = age

# 创建对象
my_dog = Dog("旺财", 3)
print(my_dog.name)  # 旺财
print(my_dog.age)    # 3

# 动态添加属性
my_dog.color = "棕色"
print(my_dog.color)  # 棕色

实例方法#

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def bark(self):
        """实例方法"""
        print(f"{self.name}在叫:汪汪!")
    
    def get_info(self):
        return f"{self.name}今年{self.age}岁"

my_dog = Dog("旺财", 3)
my_dog.bark()        # 旺财在叫:汪汪!
print(my_dog.get_info())  # 旺财今年3岁

类属性和实例属性#

类属性#

类属性属于类本身,所有实例共享。

class Dog:
    # 类属性
    species = "犬科"
    count = 0
    
    def __init__(self, name):
        self.name = name
        Dog.count += 1  # 访问类属性

dog1 = Dog("旺财")
dog2 = Dog("小黑")

print(dog1.species)  # 犬科
print(dog2.species)  # 犬科
print(Dog.count)     # 2(所有实例共享)

实例属性 vs 类属性#

class Dog:
    species = "犬科"  # 类属性
    
    def __init__(self, name):
        self.name = name  # 实例属性

dog1 = Dog("旺财")
dog2 = Dog("小黑")

# 修改类属性会影响所有实例
Dog.species = "哺乳动物"
print(dog1.species)  # 哺乳动物
print(dog2.species)  # 哺乳动物

# 通过实例修改类属性(实际上是创建了实例属性)
dog1.species = "犬科"
print(dog1.species)   # 犬科(实例属性)
print(dog2.species)   # 哺乳动物(类属性)
print(Dog.species)    # 哺乳动物(类属性未改变)

方法类型(重点难点)#

Python 中有三种方法类型:实例方法、类方法和静态方法。理解它们的区别和使用场景是掌握面向对象编程的关键。

实例方法(Instance Method)#

实例方法是最常见的方法类型,第一个参数必须是 self(指向实例本身)。实例方法可以访问和修改实例属性和类属性。

特点:

  • 第一个参数是 self(约定名称,可以是其他名称但不推荐)
  • 可以访问实例属性(通过 self
  • 可以访问类属性(通过 self 或类名)
  • 必须通过实例调用(虽然可以通过类调用,但不推荐)
class Dog:
    species = "犬科"  # 类属性
    
    def __init__(self, name, age):
        self.name = name  # 实例属性
        self.age = age
    
    def bark(self):  # 实例方法
        """访问实例属性"""
        print(f"{self.name}在叫:汪汪!")
    
    def get_info(self):
        """访问实例属性和类属性"""
        return f"{self.name}{self.species},今年{self.age}岁"
    
    def have_birthday(self):
        """修改实例属性"""
        self.age += 1
        print(f"{self.name}过生日了,现在{self.age}岁")

my_dog = Dog("旺财", 3)
my_dog.bark()              # 旺财在叫:汪汪!
print(my_dog.get_info())   # 旺财是犬科,今年3岁
my_dog.have_birthday()     # 旺财过生日了,现在4岁

# 通过类调用实例方法(不推荐,需要手动传入实例)
Dog.bark(my_dog)  # 旺财在叫:汪汪!

调用方式:

  • 推荐:instance.method() - Python 自动将实例作为 self 传入
  • 不推荐:Class.method(instance) - 需要手动传入实例

类方法(Class Method)#

类方法使用 @classmethod 装饰器,第一个参数必须是 cls(指向类本身)。类方法可以访问和修改类属性,但不能直接访问实例属性。

特点:

  • 使用 @classmethod 装饰器
  • 第一个参数是 cls(约定名称,指向类本身)
  • 可以访问类属性(通过 cls
  • 不能直接访问实例属性(因为没有 self
  • 可以通过类或实例调用(推荐通过类调用)
  • 常用于替代构造函数或操作类级别的数据
class Dog:
    count = 0  # 类属性:记录创建的实例数量
    all_dogs = []  # 类属性:存储所有实例
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
        Dog.count += 1
        Dog.all_dogs.append(self)
    
    @classmethod
    def get_count(cls):
        """获取创建的实例数量"""
        return cls.count
    
    @classmethod
    def create_from_string(cls, string):
        """类方法:替代构造函数(工厂方法)"""
        # 从字符串解析数据并创建实例
        parts = string.split()
        name = parts[0]
        age = int(parts[1]) if len(parts) > 1 else 0
        return cls(name, age)  # 使用 cls 而不是硬编码类名,支持继承
    
    @classmethod
    def create_puppy(cls, name):
        """类方法:创建特定类型的实例"""
        return cls(name, 0)  # 幼犬年龄为0
    
    @classmethod
    def get_all_names(cls):
        """获取所有实例的名称"""
        return [dog.name for dog in cls.all_dogs]

# 使用类方法
dog1 = Dog("旺财", 3)
dog2 = Dog("小黑", 5)

print(Dog.get_count())  # 2(通过类调用)
print(dog1.get_count())  # 2(通过实例调用也可以,但不推荐)

# 使用类方法作为替代构造函数
dog3 = Dog.create_from_string("小白 2")
print(dog3.name, dog3.age)  # 小白 2

dog4 = Dog.create_puppy("小黄")
print(dog4.name, dog4.age)  # 小黄 0

print(Dog.get_all_names())  # ['旺财', '小黑', '小白', '小黄']

类方法在继承中的优势:

class Animal:
    @classmethod
    def create(cls, name):
        """使用 cls 而不是硬编码类名"""
        return cls(name)  # 如果子类调用,会创建子类实例

class Dog(Animal):
    def __init__(self, name):
        self.name = name
        print(f"创建了狗:{name}")

class Cat(Animal):
    def __init__(self, name):
        self.name = name
        print(f"创建了猫:{name}")

# 使用类方法,会根据调用的类创建对应类型的实例
dog = Dog.create("旺财")  # 创建了狗:旺财
cat = Cat.create("小花")  # 创建了猫:小花

静态方法(Static Method)#

静态方法使用 @staticmethod 装饰器,不需要 selfcls 参数。静态方法与类相关,但不依赖类或实例的状态。

特点:

  • 使用 @staticmethod 装饰器
  • 不需要 selfcls 参数
  • 不能直接访问类属性或实例属性
  • 可以通过类或实例调用
  • 常用于工具函数,逻辑上与类相关但不需要访问类或实例数据
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    @staticmethod
    def is_valid_name(name):
        """验证名称是否有效(与类相关但不依赖实例)"""
        return isinstance(name, str) and len(name) > 0 and name.isalpha()
    
    @staticmethod
    def calculate_dog_years(human_years):
        """将狗年龄转换为人类年龄"""
        if human_years <= 2:
            return human_years * 10.5
        else:
            return 21 + (human_years - 2) * 4
    
    @staticmethod
    def format_dog_info(name, age):
        """格式化狗的信息"""
        return f"名称:{name},年龄:{age}岁"

# 通过类调用(推荐)
print(Dog.is_valid_name("旺财"))      # True
print(Dog.is_valid_name("123"))       # False
print(Dog.calculate_dog_years(3))     # 25.0
print(Dog.format_dog_info("旺财", 3))  # 名称:旺财,年龄:3岁

# 通过实例调用(也可以,但不推荐)
my_dog = Dog("旺财", 3)
print(my_dog.is_valid_name("小黑"))   # True

方法绑定机制#

理解 Python 的方法绑定机制有助于更好地理解三种方法类型:

class MyClass:
    class_attr = "类属性"
    
    def __init__(self):
        self.instance_attr = "实例属性"
    
    def instance_method(self):
        return f"实例方法:{self.instance_attr}"
    
    @classmethod
    def class_method(cls):
        return f"类方法:{cls.class_attr}"
    
    @staticmethod
    def static_method():
        return "静态方法"

obj = MyClass()

# 方法绑定
print(type(obj.instance_method))   # <class 'method'>(绑定方法)
print(type(MyClass.instance_method))  # <class 'function'>(未绑定函数)

print(type(obj.class_method))      # <class 'method'>(绑定方法)
print(type(MyClass.class_method))  # <class 'method'>(绑定方法)

print(type(obj.static_method))    # <class 'function'>(普通函数)
print(type(MyClass.static_method))  # <class 'function'>(普通函数)

# 调用方式
print(obj.instance_method())      # 自动传入 self
print(MyClass.instance_method(obj))  # 手动传入实例

print(obj.class_method())         # 自动传入 cls(实际是 MyClass)
print(MyClass.class_method())     # 自动传入 cls(MyClass)

print(obj.static_method())        # 不传入任何参数
print(MyClass.static_method())    # 不传入任何参数

选择指南#

何时使用实例方法:

  • 需要访问或修改实例属性
  • 操作特定实例的数据
  • 大多数常规方法都是实例方法

何时使用类方法:

  • 需要访问或修改类属性
  • 创建替代构造函数(工厂方法)
  • 需要根据子类动态创建实例(使用 cls 而不是硬编码类名)
  • 操作类级别的数据(如计数器、注册表等)

何时使用静态方法:

  • 方法与类相关,但不依赖类或实例的状态
  • 工具函数,逻辑上属于类但不需要访问类数据
  • 可以独立于类存在的函数,但放在类中更有组织性
  • 避免创建不必要的实例来调用方法

常见错误和注意事项#

class Example:
    class_attr = "类属性"
    
    def __init__(self):
        self.instance_attr = "实例属性"
    
    def instance_method(self):
        # ✅ 正确:访问实例属性
        print(self.instance_attr)
        # ✅ 正确:访问类属性
        print(self.class_attr)
    
    @classmethod
    def class_method(cls):
        # ❌ 错误:不能访问实例属性(没有 self)
        # print(cls.instance_attr)  # AttributeError
        
        # ✅ 正确:访问类属性
        print(cls.class_attr)
        
        # ⚠️ 注意:可以通过硬编码类名访问,但不推荐
        print(Example.class_attr)  # 可以,但不推荐
    
    @staticmethod
    def static_method():
        # ❌ 错误:不能访问实例属性
        # print(self.instance_attr)  # NameError: name 'self' is not defined
        
        # ⚠️ 技术上可以访问类属性,但不推荐(破坏了封装性)
        # print(Example.class_attr)  # 可以,但不推荐
        
        # ✅ 正确:不访问任何类或实例数据
        print("这是静态方法")

特殊方法(魔术方法)#

特殊方法以双下划线开头和结尾,用于定义类的行为。

__init____str__#

class Dog:
    def __init__(self, name, age):
        """初始化方法"""
        self.name = name
        self.age = age
    
    def __str__(self):
        """字符串表示(用户友好)"""
        return f"Dog(name={self.name}, age={self.age})"
    
    def __repr__(self):
        """对象表示(开发者友好)"""
        return f"Dog('{self.name}', {self.age})"

my_dog = Dog("旺财", 3)
print(my_dog)        # Dog(name=旺财, age=3)(调用__str__)
print(repr(my_dog))  # Dog('旺财', 3)(调用__repr__)

比较运算符#

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):
        """等于 =="""
        return self.age == other.age
    
    def __lt__(self, other):
        """小于 <"""
        return self.age < other.age
    
    def __le__(self, other):
        """小于等于 <="""
        return self.age <= other.age

dog1 = Dog("旺财", 3)
dog2 = Dog("小黑", 5)

print(dog1 == dog2)  # False
print(dog1 < dog2)   # True

算术运算符#

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        """加法 +"""
        return Point(self.x + other.x, self.y + other.y)
    
    def __sub__(self, other):
        """减法 -"""
        return Point(self.x - other.x, self.y - other.y)
    
    def __mul__(self, scalar):
        """乘法 *"""
        return Point(self.x * scalar, self.y * scalar)
    
    def __str__(self):
        return f"Point({self.x}, {self.y})"

p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = p1 + p2
print(p3)  # Point(4, 6)

其他常用特殊方法#

class MyList:
    def __init__(self, items):
        self.items = items
    
    def __len__(self):
        """len()函数"""
        return len(self.items)
    
    def __getitem__(self, index):
        """索引访问"""
        return self.items[index]
    
    def __setitem__(self, index, value):
        """索引赋值"""
        self.items[index] = value
    
    def __contains__(self, item):
        """in运算符"""
        return item in self.items
    
    def __call__(self):
        """使对象可调用"""
        return "对象被调用了"

my_list = MyList([1, 2, 3])
print(len(my_list))      # 3
print(my_list[0])       # 1
my_list[0] = 10
print(2 in my_list)     # True
print(my_list())        # 对象被调用了

继承(重点难点)#

继承允许一个类继承另一个类的属性和方法。

所有类都继承自 object,而 object 提供了一些基础的特殊方法(如 __init__、__str__、__repr__ 等),所以即使不显式继承,也能使用这些基础方法

继承特殊方法的规则#

  • 子类会继承父类的所有方法,包括特殊方法(魔术方法)

  • 如果子类没有重写特殊方法,就会使用父类的版本

  • 如果子类重写了特殊方法,就会使用子类的版本

基本继承#

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        return "动物在叫"

class Dog(Animal):
    def speak(self):
        """重写父类方法"""
        return f"{self.name}在叫:汪汪!"

class Cat(Animal):
    def speak(self):
        """重写父类方法"""
        return f"{self.name}在叫:喵喵!"

dog = Dog("旺财")
cat = Cat("小花")

print(dog.speak())  # 旺财在叫:汪汪!
print(cat.speak())  # 小花在叫:喵喵!

super() 函数#

super() 用于调用父类的方法。

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

class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)  # 调用父类的__init__
        self.breed = breed

dog = Dog("旺财", "金毛")
print(dog.name)   # 旺财
print(dog.breed)  # 金毛

多重继承#

Python 支持多重继承。

class Flyable:
    def fly(self):
        return "飞"

class Swimmable:
    def swim(self):
        return "游"

class Duck(Flyable, Swimmable):
    pass

duck = Duck()
print(duck.fly())   # 飞
print(duck.swim())  # 游

方法解析顺序(MRO)#

MRO 决定了在多重继承中方法查找的顺序。

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

class B(A):
    def method(self):
        return "B"

class C(A):
    def method(self):
        return "C"

class D(B, C):
    pass

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

d = D()
print(d.method())  # B(找到就返回)

多态#

多态允许不同类的对象对同一消息做出不同的响应。

class Animal:
    def speak(self):
        pass

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

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

def make_sound(animal):
    """多态函数"""
    print(animal.speak())

dog = Dog()
cat = Cat()

make_sound(dog)  # 汪汪!
make_sound(cat)  # 喵喵!

属性访问控制#

私有属性(约定)#

Python 使用单下划线前缀表示"内部使用"(约定,不是强制)。

class BankAccount:
    def __init__(self, balance):
        self._balance = balance  # 约定:内部使用
    
    def get_balance(self):
        return self._balance

account = BankAccount(1000)
print(account._balance)  # 仍然可以访问(只是约定)

名称改写(Name Mangling)#

使用双下划线前缀会触发名称改写,使属性更难直接访问。

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # 名称改写
    
    def get_balance(self):
        return self.__balance

account = BankAccount(1000)
# print(account.__balance)  # 错误!AttributeError
print(account._BankAccount__balance)  # 1000(仍然可以访问,但更困难)
print(account.get_balance())  # 1000(推荐方式)

属性装饰器(Property)#

使用 @property 装饰器可以将方法转换为属性访问,实现数据封装和验证。

三种装饰器#

  • @property:定义 getter(读取器)
  • @属性名.setter:定义 setter(设置器)
  • @属性名.deleter:定义 deleter(删除器)

基本示例#

class Circle:
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def radius(self):
        """获取半径"""
        return self._radius
    
    @radius.setter
    def radius(self, value):
        """设置半径(带验证)"""
        if value < 0:
            raise ValueError("半径不能为负数")
        self._radius = value
    
    @radius.deleter
    def radius(self):
        """删除半径"""
        del self._radius
    
    @property
    def area(self):
        """计算面积(只读属性,无setter)"""
        return 3.14159 * self._radius ** 2

circle = Circle(5)
print(circle.radius)      # 5(像属性一样访问,无需括号)
print(circle.area)        # 78.53975

circle.radius = 10        # 使用setter
print(circle.area)        # 314.159

del circle.radius         # 使用deleter
# circle.area = 100       # 错误!只读属性(没有setter)

使用规则#

  1. 命名规则@property@属性名.setter@属性名.deleter 的方法名必须相同
  2. 访问方式
    • Getter:obj.属性(无括号,像普通属性)
    • Setter:obj.属性 = 值
    • Deleter:del obj.属性
  3. 只读属性:只定义 @property,不定义 setter,即为只读
  4. 计算属性@property 方法可以返回计算后的值,而不是直接存储的值
  5. 数据验证:在 setter 中添加验证逻辑,确保数据有效性
  6. 私有属性:通常使用下划线前缀(如 _radius)表示私有属性

抽象基类#

抽象基类(ABC)定义接口规范,强制子类实现特定方法,不能直接实例化。

基本用法#

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def speak(self):
        """抽象方法,子类必须实现"""
        pass
    
    @abstractmethod
    def move(self):
        """抽象方法,子类必须实现"""
        pass
    
    def sleep(self):
        """普通方法,子类可以直接使用"""
        return "睡觉中..."

class Dog(Animal):
    def speak(self):
        return "汪汪!"
    
    def move(self):
        return "跑步"

class Cat(Animal):
    def speak(self):
        return "喵喵!"
    
    def move(self):
        return "跳跃"

# animal = Animal()  # 错误!不能实例化抽象类
dog = Dog()
print(dog.speak())  # 汪汪!
print(dog.sleep())  # 睡觉中...

抽象属性#

from abc import ABC, abstractmethod

class Shape(ABC):
    @property
    @abstractmethod
    def area(self):
        """抽象属性,子类必须实现"""
        pass
    
    @abstractmethod
    def draw(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def area(self):
        return 3.14159 * self._radius ** 2
    
    def draw(self):
        return f"绘制半径为{self._radius}的圆"

使用规则#

  1. 继承 ABC:类必须继承 ABC 或使用 metaclass=ABCMeta
  2. 抽象方法:使用 @abstractmethod 装饰器标记,子类必须实现
  3. 不能实例化:包含抽象方法的类不能直接创建实例
  4. 强制实现:子类必须实现所有抽象方法,否则仍是抽象类
  5. 普通方法:抽象基类中可以定义普通方法,子类可以直接使用
  6. 接口规范:用于定义接口契约,确保子类实现必要的方法

最佳实践#

  1. 使用有意义的类名(PascalCase)
  2. 每个类应该有文档字符串
  3. 合理使用继承,避免过度继承
  4. 优先使用组合而非继承
  5. 使用属性装饰器控制属性访问
  6. 理解 MRO 和多重继承

下一步#

掌握了面向对象编程后,可以学习:

  1. 异常处理 - 学习如何处理错误和异常
  2. 模块和包 - 学习如何组织代码文件