Python面向对象编程

1. 引言

面向对象编程(Object-Oriented Programming, OOP)是一种程序设计范式,它使用"对象"作为基本单元来构建程序。Python作为一门多范式语言,完全支持面向对象编程。本教程将详细介绍Python中的面向对象概念,包括类、对象、继承、多态和封装等核心特性。

2. 类和对象

理解类和对象

类是创建对象的蓝图或模板,定义了对象的属性和方法

对象是类的实例,包含实际的数据

定义类

使用`class`关键字定义类,类名通常采用驼峰命名法(PascalCase)

class Dog:
    # 类属性
    species = "Canis familiaris"

    # 初始化方法/构造函数
    def __init__(self, name, age):
        # 实例属性
        self.name = name
        self.age = age

    # 实例方法
    def bark(self):
        return f"{self.name} says woof!"

    # 类方法
    @classmethod
    def get_species(cls):
        return cls.species

创建对象

通过调用类名并传递必要参数来创建对象

# 创建Dog类的实例
dog1 = Dog("Buddy", 3)
dog2 = Dog("Lucy", 5)

# 访问实例属性
print(dog1.name)  # 输出: Buddy
print(dog2.age)   # 输出: 5

# 调用实例方法
print(dog1.bark())  # 输出: Buddy says woof!

# 访问类属性和方法
print(dog1.species)        # 输出: Canis familiaris
print(Dog.get_species())   # 输出: Canis familiaris

3. 继承

理解继承

继承允许我们定义一个类(子类)继承另一个类(父类)的属性和方法

子类可以重用父类的代码,并添加新功能或修改现有功能

创建子类

在定义类时,将父类名放在括号中

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

    def speak(self):
        raise NotImplementedError("Subclass must implement abstract method")

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

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

使用继承

# 创建子类实例
cat = Cat("Whiskers")
dog = Dog("Rex")

# 调用继承的方法
print(cat.speak())  # 输出: Whiskers says meow!
print(dog.speak())  # 输出: Rex says woof!

4. 多态

理解多态

多态意味着"多种形态",不同类的对象可以对相同的方法调用做出不同的响应

允许我们使用统一的接口处理不同类型的对象

多态的实现

def animal_sound(animal):
    print(animal.speak())

# 使用相同接口处理不同类型的对象
animal_sound(Cat("Misty"))  # 输出: Misty says meow!
animal_sound(Dog("Max"))    # 输出: Max says woof!

5. 封装

理解封装

封装是将数据(属性)和操作数据的方法捆绑在一起

隐藏内部实现细节,只暴露必要的接口

访问控制

公有成员:任何地方都可以访问

私有成员:名称以双下划线开头(如`__private_var`)

保护成员:名称以单下划线开头(如`_protected_var`)

class BankAccount:
    def __init__(self, balance=0):
        self.__balance = balance  # 私有属性

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

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            return amount
        return 0

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

使用封装

account = BankAccount(100)
account.deposit(50)
print(account.get_balance())  # 输出: 150

# 尝试直接访问私有属性会失败
try:
    print(account.__balance)
except AttributeError as e:
    print(e)  # 输出: 'BankAccount' object has no attribute '__balance'

6. 特殊方法

理解特殊方法

特殊方法也称为魔术方法,以双下划线开头和结尾(如`__init__`)

它们允许我们自定义类的行为,使其与Python内置函数或操作符兼容

常用特殊方法

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):
        return Vector(self.x + other.x, self.y + other.y)

    # 相等比较
    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    # 长度/大小
    def __len__(self):
        return int((self.x**2 + self.y**2)**0.5)

使用特殊方法

v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1)          # 输出: Vector(3, 4)
print(repr(v2))    # 输出: Vector(1, 2)

v3 = v1 + v2
print(v3)          # 输出: Vector(4, 6)

print(v1 == Vector(3, 4))  # 输出: True
print(len(v1))     # 输出: 5

7. 静态方法和类方法

静态方法

使用`@staticmethod`装饰器定义

不访问类或实例状态,类似于普通函数但属于类的命名空间

类方法

使用`@classmethod`装饰器定义

接收类作为第一个参数(通常命名为`cls`)

class MathUtils:
    @staticmethod
    def add(a, b):
        return a + b

    @classmethod
    def multiply(cls, a, b):
        print(f"Called from {cls.__name__} class")
        return a * b

# 调用静态方法
print(MathUtils.add(5, 3))  # 输出: 8

# 调用类方法
print(MathUtils.multiply(4, 7))  # 输出: Called from MathUtils class 和 28

8. 属性和描述符

属性(Property)

使用`@property`装饰器将方法转换为属性

提供对属性的受控访问

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("Radius must be non-negative")
        self._radius = value

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

# 使用属性
circle = Circle(5)
print(circle.radius)  # 输出: 5
print(circle.area)    # 输出: 78.53975

circle.radius = 7
print(circle.area)    # 输出: 153.93791

try:
    circle.radius = -2
except ValueError as e:
    print(e)  # 输出: Radius must be non-negative

描述符

实现`__get__`, `__set__`或`__delete__`方法的类

用于控制对属性的访问

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

    def __get__(self, obj, objtype):
        return obj.__dict__[self.name]

    def __set__(self, obj, value):
        if value < 0:
            raise ValueError(f"{self.name} must be positive")
        obj.__dict__[self.name] = value

class Product:
    price = Positive("price")

    def __init__(self, name, price):
        self.name = name
        self.price = price

# 使用描述符
p = Product("Laptop", 999)
print(p.price)  # 输出: 999

try:
    p.price = -100
except ValueError as e:
    print(e)  # 输出: price must be positive

9. 抽象基类

理解抽象基类

抽象基类(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)

# 使用抽象基类
rect = Rectangle(4, 5)
print(rect.area())      # 输出: 20
print(rect.perimeter()) # 输出: 18

try:
    shape = Shape()  # 尝试实例化抽象类
except TypeError as e:
    print(e)  # 输出: Can't instantiate abstract class Shape with abstract methods area, perimeter

10. 总结

Python面向对象编程提供了一种强大的方式来构建和组织代码,通过类和对象的概念实现了代码的模块化和重用。本教程详细介绍了类与对象的创建、继承、多态、封装、特殊方法、静态方法与类方法、属性与描述符以及抽象基类等核心概念。掌握这些技术将帮助你设计更清晰、更可维护的Python应用程序,充分利用面向对象编程的优势来构建复杂系统。随着实践的深入,你将能够更加熟练地运用这些概念来解决实际问题。

发表回复

后才能评论