Python 进阶教程:面向对象编程

1. 概述

1.1 什么是面向对象编程(OOP)

面向对象编程是一种编程范式,它使用”对象”来设计软件。对象包含数据(属性)和代码(方法)。

核心概念对比表:

概念说明示例
类(Class)对象的蓝图或模板class Dog:
对象(Object)类的实例my_dog = Dog()
属性(Attribute)对象的数据my_dog.name = "Buddy"
方法(Method)对象的函数my_dog.bark()

1.2 面向对象的三大特性

  1. 封装(Encapsulation):隐藏内部实现细节
  2. 继承(Inheritance):基于现有类创建新类
  3. 多态(Polymorphism):同一接口,不同实现

2. 类和对象

2.1 定义类

class Dog:
    """一个简单的Dog类"""
    
    # 类属性
    species = "Canis familiaris"
    
    # 初始化方法(构造器)
    def __init__(self, name, age):
        # 实例属性
        self.name = name
        self.age = age
    
    # 实例方法
    def bark(self):
        return f"{self.name} says Woof!"
    
    def description(self):
        return f"{self.name} is {self.age} years old"

2.2 创建对象(实例化)

# 创建两个Dog对象
dog1 = Dog("Buddy", 3)
dog2 = Dog("Max", 5)
​
# 访问属性
print(dog1.name)        # 输出: Buddy
print(dog2.age)         # 输出: 5
​
# 调用方法
print(dog1.bark())      # 输出: Buddy says Woof!
print(dog2.description()) # 输出: Max is 5 years old
​
# 访问类属性
print(Dog.species)      # 输出: Canis familiaris
print(dog1.species)     # 输出: Canis familiaris

2.3 __init__ 方法详解

特性说明
作用初始化对象状态
调用时机创建对象时自动调用
第一个参数必须是 self,指向新创建的对象
返回值必须返回 None(隐式返回 None)
class Person:
    def __init__(self, name, age):
        self.name = name  # 正确:使用self访问实例属性
        self.age = age
        
    # 错误示例
    def wrong_init(self, name):
        name = name  # 错误:这只是局部变量,没有赋值给实例

3. 封装

3.1 访问控制

Python没有真正的私有属性,但有以下约定:

约定语法说明访问方式
公开attribute随处可访问obj.attribute
受保护_attribute约定为内部使用obj._attribute
私有__attribute名称修饰,外部难访问_ClassName__attribute

3.2 封装示例

class BankAccount:
    def __init__(self, account_number, balance=0):
        self.account_number = account_number  # 公开属性
        self._balance = balance              # 受保护属性(约定)
        self.__pin = 1234                   # 私有属性(名称修饰)
    
    # 公开方法:获取余额
    def get_balance(self):
        return self._balance
    
    # 公开方法:存款
    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
            return True
        return False
    
    # 公开方法:取款
    def withdraw(self, amount):
        if 0 < amount <= self._balance:
            self._balance -= amount
            return True
        return False
    
    # 私有方法(约定)
    def __validate_pin(self, pin):
        return self.__pin == pin
​
# 使用封装的类
account = BankAccount("123456789", 1000)
print(account.get_balance())    # 输出: 1000
account.deposit(500)
print(account.get_balance())    # 输出: 1500
​
# 尝试访问私有属性(不推荐)
print(account._BankAccount__pin)  # 输出: 1234(但不应该这样访问)

3.3 封装的最佳实践

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius
    
    # 使用属性装饰器提供更安全的访问方式
    @property
    def celsius(self):
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        if value < -273.15:  # 绝对零度
            raise ValueError("温度不能低于绝对零度")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        return self._celsius * 9/5 + 32
​
# 使用
temp = Temperature(25)
print(temp.celsius)      # 输出: 25
print(temp.fahrenheit)   # 输出: 77.0
temp.celsius = 30       # 使用setter

4. 继承

4.1 基本继承

# 父类(基类)
class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        raise NotImplementedError("子类必须实现此方法")
    
    def sleep(self):
        return f"{self.name} is sleeping"
​
# 子类(派生类)
class Dog(Animal):
    def __init__(self, name, breed):
        # 调用父类构造器
        super().__init__(name)
        self.breed = breed
    
    # 重写父类方法
    def speak(self):
        return f"{self.name} says Woof!"
​
class Cat(Animal):
    def __init__(self, name, color):
        super().__init__(name)
        self.color = color
    
    def speak(self):
        return f"{self.name} says Meow!"
​
# 使用继承
dog = Dog("Buddy", "Golden Retriever")
cat = Cat("Whiskers", "Tabby")
​
print(dog.speak())      # 输出: Buddy says Woof!
print(cat.speak())      # 输出: Whiskers says Meow!
print(dog.sleep())      # 输出: Buddy is sleeping(继承自父类)

4.2 super() 函数

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

class Parent:
    def __init__(self, name):
        self.name = name
    
    def show(self):
        return f"Parent: {self.name}"
​
class Child(Parent):
    def __init__(self, name, age):
        # 调用父类的__init__
        super().__init__(name)
        self.age = age
    
    def show(self):
        # 调用父类的show方法
        parent_msg = super().show()
        return f"{parent_msg}, Child: {self.age}"
​
child = Child("Alice", 10)
print(child.show())  # 输出: Parent: Alice, Child: 10

4.3 多继承

Python支持多继承,但需谨慎使用:

class Flyable:
    def fly(self):
        return "Flying..."
​
class Swimmable:
    def swim(self):
        return "Swimming..."
​
# 多继承
class Duck(Flyable, Swimmable):
    def quack(self):
        return "Quack!"
​
donald = Duck()
print(donald.fly())    # 输出: Flying...
print(donald.swim())   # 输出: Swimming...
print(donald.quack())  # 输出: Quack!

4.4 方法解析顺序(MRO)

Python使用C3算法确定方法解析顺序:

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

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

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

class D(B, C):
    pass

# 查看MRO
print(D.mro())
# 输出: [<class '__main__.D'>, <class '__main__.B'>, 
#        <class '__main__.C'>, <class '__main__.A'>, <class 'object'>]

d = D()
print(d.show())  # 输出: B(按照MRO顺序)

5. 多态

5.1 什么是多态

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

class Bird:
    def fly(self):
        return "Bird flying"

class Airplane:
    def fly(self):
        return "Airplane flying"

class Kite:
    def fly(self):
        return "Kite flying"

# 多态:同一方法名,不同实现
def let_it_fly(obj):
    print(obj.fly())

# 不同对象调用同一方法
bird = Bird()
plane = Airplane()
kite = Kite()

let_it_fly(bird)    # 输出: Bird flying
let_it_fly(plane)   # 输出: Airplane flying
let_it_fly(kite)    # 输出: Kite flying

5.2 鸭子类型(Duck Typing)

Python的多态基于鸭子类型:”如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子。”

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

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

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

# 不需要共同的父类,只要有相同的方法即可
def make_animal_speak(animal):
    print(animal.speak())

animals = [Dog(), Cat(), Duck()]
for animal in animals:
    make_animal_speak(animal)
# 输出:
# Woof!
# Meow!
# Quack!

5.3 抽象基类(ABC)

使用abc模块定义抽象基类:

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass
    
    @abstractmethod
    def perimeter(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height
    
    def perimeter(self):
        return 2 * (self.width + self.height)

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2
    
    def perimeter(self):
        return 2 * 3.14 * self.radius

# 不能实例化抽象类
# shape = Shape()  # 报错: TypeError

# 可以实例化具体子类
rect = Rectangle(5, 3)
circle = Circle(4)

print(rect.area())      # 输出: 15
print(circle.area())    # 输出: 50.24

6. 特殊方法

特殊方法(魔术方法)以双下划线开头和结尾,用于实现类的特定行为。

6.1 常用特殊方法表

方法说明触发方式
__init__构造器obj = Class()
__str__字符串表示(友好)str(obj), print(obj)
__repr__字符串表示(官方)repr(obj)
__len__长度len(obj)
__getitem__索引访问obj[key]
__setitem__索引赋值obj[key] = value
__delitem__删除索引del obj[key]
__iter__迭代器for x in obj:
__contains__成员检测item in obj
__eq__相等比较obj1 == obj2
__lt__小于比较obj1 < obj2
__add__加法obj1 + obj2
__sub__减法obj1 - obj2
__mul__乘法obj1 * obj2
__enter__, __exit__上下文管理器with obj:

6.2 实现特殊方法示例

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    
    def __str__(self):
        return f"Vector({self.x}, {self.y})"
    
    def __repr__(self):
        return f"Vector({self.x}, {self.y})"
    
    def __add__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x + other.x, self.y + other.y)
        return NotImplemented
    
    def __sub__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x - other.x, self.y - other.y)
        return NotImplemented
    
    def __eq__(self, other):
        if isinstance(other, Vector):
            return self.x == other.x and self.y == other.y
        return False
    
    def __len__(self):
        # 向量的维度
        return 2
    
    def __abs__(self):
        # 向量的模
        return (self.x ** 2 + self.y ** 2) ** 0.5

# 使用特殊方法
v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1)                # 输出: Vector(3, 4)(调用__str__)
print(v1 + v2)          # 输出: Vector(4, 6)(调用__add__)
print(v1 - v2)          # 输出: Vector(2, 2)(调用__sub__)
print(v1 == v2)         # 输出: False(调用__eq__)
print(len(v1))          # 输出: 2(调用__len__)
print(abs(v1))          # 输出: 5.0(调用__abs__)

6.3 容器类特殊方法

class ShoppingCart:
    def __init__(self):
        self.items = []
    
    def add(self, item):
        self.items.append(item)
    
    def __len__(self):
        return len(self.items)
    
    def __getitem__(self, index):
        return self.items[index]
    
    def __setitem__(self, index, value):
        self.items[index] = value
    
    def __delitem__(self, index):
        del self.items[index]
    
    def __iter__(self):
        return iter(self.items)
    
    def __contains__(self, item):
        return item in self.items

# 使用容器类
cart = ShoppingCart()
cart.add("Apple")
cart.add("Banana")
cart.add("Orange")

print(len(cart))           # 输出: 3
print(cart[0])            # 输出: Apple
print("Banana" in cart)   # 输出: True

for item in cart:
    print(item)            # 输出: Apple, Banana, Orange

7. 属性装饰器

7.1 @property 装饰器

@property装饰器用于将方法转换为属性访问:

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name
    
    @property
    def full_name(self):
        """只读属性"""
        return f"{self.first_name} {self.last_name}"
    
    @property
    def email(self):
        """只读属性"""
        return f"{self.first_name}.{self.last_name}@example.com"
    
    @full_name.setter
    def full_name(self, name):
        """可写属性"""
        first, last = name.split()
        self.first_name = first
        self.last_name = last

# 使用@property
person = Person("John", "Doe")
print(person.full_name)    # 输出: John Doe(像属性一样访问)
print(person.email)       # 输出: John.Doe@example.com

person.full_name = "Jane Smith"  # 使用setter
print(person.first_name)   # 输出: Jane
print(person.last_name)    # 输出: Smith

7.2 属性装饰器的最佳实践

class Circle:
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def radius(self):
        """半径的getter"""
        return self._radius
    
    @radius.setter
    def radius(self, value):
        """半径的setter,包含验证"""
        if value <= 0:
            raise ValueError("半径必须为正数")
        self._radius = value
    
    @property
    def area(self):
        """面积的只读属性"""
        return 3.14 * self._radius ** 2
    
    @property
    def diameter(self):
        """直径的只读属性"""
        return self._radius * 2
    
    @diameter.setter
    def diameter(self, value):
        """通过直径设置半径"""
        self.radius = value / 2

# 使用
circle = Circle(5)
print(circle.radius)    # 输出: 5
print(circle.area)      # 输出: 78.5
print(circle.diameter)  # 输出: 10

circle.diameter = 14
print(circle.radius)    # 输出: 7.0

8. 类方法和静态方法

8.1 实例方法、类方法和静态方法对比

方法类型装饰器第一个参数访问实例属性访问类属性调用方式
实例方法selfobj.method()
类方法@classmethodclsClass.method()obj.method()
静态方法@staticmethodClass.method()obj.method()

8.2 类方法(@classmethod

类方法用于操作类本身,而不是实例:

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
    
    def __str__(self):
        return f"{self.year}-{self.month:02d}-{self.day:02d}"
    
    # 类方法:作为备选构造器
    @classmethod
    def from_string(cls, date_string):
        """从字符串创建Date对象"""
        year, month, day = map(int, date_string.split('-'))
        return cls(year, month, day)
    
    @classmethod
    def today(cls):
        """创建表示今天的Date对象"""
        import datetime
        today = datetime.date.today()
        return cls(today.year, today.month, today.day)

# 使用类方法
date1 = Date(2024, 5, 15)
date2 = Date.from_string("2024-12-25")  # 使用类方法创建
date3 = Date.today()                     # 使用类方法创建

print(date1)  # 输出: 2024-05-15
print(date2)  # 输出: 2024-12-25
print(date3)  # 输出: 2024-xx-xx(今天日期)

8.3 静态方法(@staticmethod

静态方法与类相关,但不访问类或实例的状态:

import math

class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b
    
    @staticmethod
    def subtract(a, b):
        return a - b
    
    @staticmethod
    def is_prime(n):
        """检查一个数是否为素数"""
        if n <= 1:
            return False
        if n <= 3:
            return True
        if n % 2 == 0 or n % 3 == 0:
            return False
        
        i = 5
        while i * i <= n:
            if n % i == 0 or n % (i + 2) == 0:
                return False
            i += 6
        return True

# 使用静态方法
print(MathUtils.add(5, 3))           # 输出: 8
print(MathUtils.subtract(10, 4))     # 输出: 6
print(MathUtils.is_prime(17))        # 输出: True
print(MathUtils.is_prime(15))        # 输出: False

8.4 实际应用示例

class Employee:
    # 类属性
    company = "Tech Corp"
    employee_count = 0
    
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
        Employee.employee_count += 1
    
    # 实例方法
    def display(self):
        return f"Employee: {self.name}, Salary: {self.salary}"
    
    # 类方法:修改类属性
    @classmethod
    def set_company(cls, company_name):
        cls.company = company_name
    
    # 类方法:获取类属性
    @classmethod
    def get_employee_count(cls):
        return cls.employee_count
    
    # 静态方法:工具函数
    @staticmethod
    def is_valid_salary(salary):
        return salary > 0

# 使用
emp1 = Employee("Alice", 50000)
emp2 = Employee("Bob", 60000)

print(Employee.get_employee_count())  # 输出: 2
print(Employee.company)               # 输出: Tech Corp

Employee.set_company("New Tech Corp")
print(emp1.company)                   # 输出: New Tech Corp

print(Employee.is_valid_salary(50000))  # 输出: True
print(Employee.is_valid_salary(-1000))  # 输出: False

9. 实战示例

9.1 示例1:简单的学生管理系统

class Student:
    def __init__(self, student_id, name, age):
        self.student_id = student_id
        self.name = name
        self.age = age
        self.courses = []
    
    def enroll(self, course):
        if course not in self.courses:
            self.courses.append(course)
            return True
        return False
    
    def get_info(self):
        return {
            "ID": self.student_id,
            "Name": self.name,
            "Age": self.age,
            "Courses": self.courses
        }

class Course:
    def __init__(self, course_id, name, credits):
        self.course_id = course_id
        self.name = name
        self.credits = credits
        self.students = []
    
    def add_student(self, student):
        if student not in self.students:
            self.students.append(student)
            student.enroll(self.name)
            return True
        return False
    
    def get_info(self):
        return {
            "ID": self.course_id,
            "Name": self.name,
            "Credits": self.credits,
            "Students": [s.name for s in self.students]
        }

# 使用学生管理系统
student1 = Student("S001", "Alice", 20)
student2 = Student("S002", "Bob", 21)

course1 = Course("C001", "Python Programming", 3)
course2 = Course("C002", "Data Structures", 4)

# 学生选课
course1.add_student(student1)
course1.add_student(student2)
course2.add_student(student1)

# 查看信息
print(student1.get_info())
# 输出: {'ID': 'S001', 'Name': 'Alice', 'Age': 20, 'Courses': ['Python Programming', 'Data Structures']}

print(course1.get_info())
# 输出: {'ID': 'C001', 'Name': 'Python Programming', 'Credits': 3, 'Students': ['Alice', 'Bob']}

9.2 示例2:简单的数据库连接池

import threading
import time

class DatabaseConnection:
    """模拟数据库连接"""
    def __init__(self, connection_id):
        self.connection_id = connection_id
        self.in_use = False
    
    def connect(self):
        print(f"Connection {self.connection_id} connected")
    
    def disconnect(self):
        print(f"Connection {self.connection_id} disconnected")
    
    def execute(self, query):
        print(f"Connection {self.connection_id} executing: {query}")
        return f"Result from connection {self.connection_id}"

class ConnectionPool:
    """数据库连接池"""
    def __init__(self, max_connections):
        self.max_connections = max_connections
        self.connections = []
        self.lock = threading.Lock()
        
        # 初始化连接池
        for i in range(max_connections):
            conn = DatabaseConnection(i)
            self.connections.append(conn)
    
    def get_connection(self):
        """从连接池获取连接"""
        with self.lock:
            while True:
                for conn in self.connections:
                    if not conn.in_use:
                        conn.in_use = True
                        conn.connect()
                        return conn
                # 所有连接都在使用,等待
                print("Waiting for available connection...")
                time.sleep(0.1)
    
    def release_connection(self, conn):
        """释放连接到连接池"""
        with self.lock:
            conn.in_use = False
            conn.disconnect()

# 使用连接池
pool = ConnectionPool(max_connections=2)

def worker(worker_id):
    """工作线程函数"""
    conn = pool.get_connection()
    try:
        result = conn.execute(f"SELECT * FROM table WHERE id = {worker_id}")
        print(f"Worker {worker_id}: {result}")
    finally:
        pool.release_connection(conn)

# 创建多个线程使用连接池
threads = []
for i in range(5):
    t = threading.Thread(target=worker, args=(i,))
    threads.append(t)
    t.start()

for t in threads:
    t.join()

10. 最佳实践

10.1 命名规范

元素约定示例
类名PascalCaseMyClass
方法名snake_caseget_name()
属性名snake_caseself.first_name
私有属性前缀 __self.__private_attr
受保护属性前缀 _self._internal_attr
常量UPPER_CASEMAX_SIZE = 100

10.2 设计原则

  1. 单一职责原则(SRP):一个类应该只有一个引起变化的原因
  2. 开放封闭原则(OCP):对扩展开放,对修改封闭
  3. 里氏替换原则(LSP):子类必须能够替换父类
  4. 接口隔离原则(ISP):客户端不应该依赖它不需要的接口
  5. 依赖倒置原则(DIP):依赖抽象而不是具体实现

10.3 代码组织建议

# 良好的类组织结构示例
class ExampleClass:
    """类的文档字符串"""
    
    # 1. 类属性
    class_attribute = "value"
    
    # 2. 初始化方法
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
    
    # 3. 特殊方法
    def __str__(self):
        pass
    
    def __repr__(self):
        pass
    
    # 4. 属性装饰器
    @property
    def property_name(self):
        pass
    
    # 5. 公共方法
    def public_method(self):
        pass
    
    # 6. 私有方法
    def _private_method(self):
        pass
    
    # 7. 类方法
    @classmethod
    def class_method(cls):
        pass
    
    # 8. 静态方法
    @staticmethod
    def static_method():
        pass

10.4 常见陷阱和解决方案

陷阱问题解决方案
可变默认参数def func(x=[])使用 x=None,在函数内检查
属性隐藏子类属性覆盖父类属性使用 super() 调用父类方法
循环导入两个模块相互导入重构代码或使用局部导入
深度继承继承层次过深使用组合代替继承
过度使用 @property简单属性也使用装饰器只在需要验证或计算时使用
# 陷阱1:可变默认参数
class WrongExample:
    def __init__(self, items=[]):  # 错误!
        self.items = items

class RightExample:
    def __init__(self, items=None):  # 正确
        if items is None:
            self.items = []
        else:
            self.items = items

# 陷阱2:属性隐藏
class Parent:
    def show(self):
        print("Parent")

class Child(Parent):
    def show(self):
        super().show()  # 正确:调用父类方法
        print("Child")

总结

Python面向对象编程提供了强大的工具来组织和管理代码。关键要点:

  1. 类与对象:类是蓝图,对象是实例
  2. 封装:使用命名约定和属性装饰器控制访问
  3. 继承:重用代码,建立is-a关系
  4. 多态:同一接口,不同实现
  5. 特殊方法:自定义类的行为
  6. 装饰器:@property、@classmethod、@staticmethod
  7. 最佳实践:遵循命名规范,应用设计原则

通过合理运用这些概念,可以编写出更清晰、更易维护的Python代码。


参考资料

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容