跳转至

Python面向对象编程

面向对象编程是一种非常流行的编程范式(programming paradigm),所谓编程范式就是程序设计的方法论,简单的说就是程序员对程序的认知和理解以及他们编写代码的方式。 用一句话来概括面向对象编程:面向对象编程即把一组数据和处理数据的方法组成对象,把行为相同的对象归纳为类,通过封装隐藏对象的内部细节,通过继承实现类的特化和泛化,通过多态实现基于对象类型的动态分派。 关键词:对象(object)、类(class)、封装(encapsulation)、继承(inheritance)、多态(polymorphism)。

1 类和对象基础

在面向对象编程中,类是一个抽象的概念,对象是一个具体的概念。我们把同一类对象的共同特征抽取出来就是一个类,比如我们经常说的人类,这是一个抽象概念,而我们每个人就是人类的这个抽象概念下的实实在在的存在,也就是一个对象。简而言之,类是对象的蓝图和模板,对象是类的实例,是可以接受消息的实体。 在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类。对象的属性是对象的静态特征,对象的行为是对象的动态特征。

1.1 类的定义

在 Python 语言中,使用class关键字加上类名来定义类,通过缩进可以确定类的代码块,就如同定义函数那样。 在类的代码块中需要写一些函数,类是一个抽象概念,那么这些函数就是我们对一类对象共同的动态特征的提取。写在类里面的函数我们通常称之为方法,方法就是对象的行为,也就是对象可以接收的消息。方法的第一个参数通常都是self,它代表了接收这个消息的对象本身,代码如下所示:

 class Student:

    def study(self, course_name):
        print(f'学生正在学习{course_name}.')

    def play(self):
        print(f'学生正在玩游戏.')

1.2 创建和使用对象

在定义好一个类之后,可以使用构造器语法来创建对象,在类的名字后跟上圆括号就是所谓的构造器语法,下面的代码创建了两个学生对象,一个赋值给变量stu1,一个赋值给变量stu2。代码如下所示:

stu1 = Student()
stu2 = Student()
接下来,尝试给对象发消息,即调用对象的方法。刚才的Student类中我们定义了study和play两个方法,两个方法的第一个参数self代表了接收消息的学生对象,study方法的第二个参数是学习的课程名称。Python中,给对象发消息有两种方式,如下面的代码:
# 通过“类.方法”调用方法
# 第一个参数是接收消息的对象
# 第二个参数是学习的课程名称
Student.study(stu1, 'Python程序设计')    # 学生正在学习Python程序设计.
# 通过“对象.方法”调用方法
# 点前面的对象就是接收消息的对象
# 只需要传入第二个参数课程名称
stu1.study('Python程序设计')             # 学生正在学习Python程序设计.

Student.play(stu2)                      # 学生正在玩游戏.
stu2.play()                             # 学生正在玩游戏. 

1.3 初始化方法

刚才创建的学生对象只有行为没有属性,如果要给学生对象定义属性,可以修改Student类,为其添加一个名为__init__的方法。在我们调用Student类的构造器创建对象时,首先会在内存中获得保存学生对象所需的内存空间,然后通过自动执行__init__方法,完成对内存的初始化操作,也就是把数据放到内存空间中。所以我们可以通过给Student类添加__init__方法的方式为学生对象指定属性,同时完成对属性赋初始值的操作,正因如此,__init__方法通常也被称为初始化方法。 对上面的Student类稍作修改,给学生对象添加name(姓名)和age(年龄)两个属性。

class Student:
    """学生"""

    def __init__(self, name, age):
        """初始化方法"""
        self.name = name
        self.age = age

    def study(self, course_name):
        """学习"""
        print(f'{self.name}正在学习{course_name}.')

    def play(self):
        """玩耍"""
        print(f'{self.name}正在玩游戏.')


# 调用Student类的构造器创建对象并传入初始化参数
stu1 = Student('小明', 14)
stu2 = Student('小红', 25)
stu1.study('Python程序设计')    # 小明正在学习Python程序设计.
stu2.play()                    # 小红正在玩游戏.

1.4 可见性和属性装饰器

在很多面向对象编程语言中,对象的属性通常会被设置为私有(private)或受保护(protected)的成员,简单的说就是不允许直接访问这些属性;对象的方法通常都是公开的(public),因为公开的方法是对象能够接受的消息,也是对象暴露给外界的调用接口,这就是所谓的访问可见性。在 Python 中,可以通过给对象属性名添加前缀下划线的方式来说明属性的访问可见性,不可以直接访问,例如,可以用__name表示一个私有属性,_name表示一个受保护属性,代码如下所示:

class Student:

    def __init__(self, name, age):
        self.__name = name
        self.__age = age

    def study(self, course_name):
        print(f'{self.__name}正在学习{course_name}.')


stu = Student('小明', 23)
stu.study('Python基础')
print(stu.__name)  
# AttributeError: 'Student' object has no attribute '__name'
在 Python 里,属性装饰器(property decorator)是一个内置装饰器,其作用是把类中的方法转变为可以像属性一样访问的特殊方法。借助属性装饰器,我们能够在不改变类原有调用方式的前提下,为属性的获取、设置以及删除操作添加额外的逻辑。下面为你详细介绍属性装饰器: 属性装饰器的基础形式是@property,它可以让一个方法在被调用时,看起来就像是直接访问属性一样,而无需使用括号:
class Circle:
    def __init__(self, radius):
        self._radius = radius  # 私有属性

    @property
    def radius(self):
        return self._radius

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

# 使用示例
c = Circle(5)
print(c.radius)  # 输出:5(直接访问方法,无需括号)
print(c.area)    # 输出:78.5(自动计算面积)
属性装饰器还能和@属性名.setter、@属性名.deleter搭配使用,从而实现对属性的设置和删除操作:
class Person:
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        """获取属性时执行的方法"""
        return self._name

    @name.setter
    def name(self, value):
        """设置属性时执行的方法"""
        if not isinstance(value, str):
            raise TypeError("Name must be a string")
        self._name = value

    @name.deleter
    def name(self):
        """删除属性时执行的方法"""
        print("Deleting name...")
        del self._name

# 使用示例
p = Person("Alice")
print(p.name)  # 输出:Alice

p.name = "Bob"  # 自动调用 @name.setter
print(p.name)  # 输出:Bob

# p.name = 123  # 会抛出 TypeError

del p.name     # 自动调用 @name.deleter
# print(p.name)  # 会抛出 AttributeError

2 继承和多态

2.1 继承

面向对象的编程语言支持在已有类的基础上创建新类,从而减少重复代码的编写。提供继承信息的类叫做父类(超类、基类),得到继承信息的类叫做子类(派生类、衍生类)。例如,我们定义一个学生类和一个老师类,我们会发现他们有大量的重复代码,而这些重复代码都是老师和学生作为人的公共属性和行为,所以在这种情况下,我们应该先定义人类,再通过继承,从人类派生出老师类和学生类,代码如下所示:

class Person:
    """人"""

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

    def eat(self):
        print(f'{self.name}正在吃饭.')

    def sleep(self):
        print(f'{self.name}正在睡觉.')


class Student(Person):
    """学生"""

    def __init__(self, name, age):
        super().__init__(name, age)

    def study(self, course_name):
        print(f'{self.name}正在学习{course_name}.')


class Teacher(Person):
    """老师"""

    def __init__(self, name, age, title):
        super().__init__(name, age)
        self.title = title

    def teach(self, course_name):
        print(f'{self.name}{self.title}正在讲授{course_name}.')



stu1 = Student('李元芳', 21)
stu2 = Student('狄仁杰', 22)
tea1 = Teacher('武则天', 35, '副教授')
stu1.eat()
stu2.sleep()
tea1.eat()
stu1.study('Python程序设计')
tea1.teach('Python程序设计')
stu2.study('数据科学导论')

'''
李元芳正在吃饭.
狄仁杰正在睡觉.
武则天正在吃饭.
李元芳正在学习Python程序设计.
武则天副教授正在讲授Python程序设计.
狄仁杰正在学习数据科学导论.
'''

2.2 多态

多态指的是不同对象对同一消息(方法调用)做出不同响应的能力,例如,多个类实现了同名方法,调用该方法时会根据对象类型执行对应的逻辑:

class Animal:
    def speak(self):
        pass

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

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

# 多态调用
def make_sound(animal):
    print(animal.speak())

dog = Dog()
cat = Cat()

make_sound(dog)  # 输出: Woof!
make_sound(cat)  # 输出: Meow!

3 魔术方法

3.1 常用魔术方法

class Book:
    def __init__(self, title, author, price):
        self.title = title
        self.author = author
        self.price = price

    def __str__(self):
        return f"{self.title} by {self.author}"

    def __repr__(self):
        return f"Book('{self.title}', '{self.author}', {self.price})"

    def __eq__(self, other):
        if not isinstance(other, Book):
            return False
        return self.title == other.title and self.author == other.author

    def __lt__(self, other):
        return self.price < other.price

4 设计模式

4.1 单例模式

class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

4.2 工厂模式

class AnimalFactory:
    @staticmethod
    def create_animal(animal_type, name):
        if animal_type == "dog":
            return Dog(name)
        elif animal_type == "cat":
            return Cat(name)
        elif animal_type == "duck":
            return Duck(name)
        else:
            raise ValueError("不支持的动物类型")

5 实践应用

5.1 实现一个简单的游戏角色系统

class Character:
    def __init__(self, name, level=1):
        self.name = name
        self.level = level
        self.health = 100
        self.experience = 0

    def gain_experience(self, amount):
        self.experience += amount
        if self.experience >= 100:
            self.level_up()

    def level_up(self):
        self.level += 1
        self.experience -= 100
        self.health = 100
        print(f"{self.name}升级到{self.level}级!")

5.2 实现一个简单的银行账户系统

class Account:
    def __init__(self, account_number, holder_name):
        self.__account_number = account_number
        self.__holder_name = holder_name
        self.__balance = 0
        self.__transactions = []

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            self.__transactions.append(f"存款: +{amount}")
            return True
        return False

    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            self.__transactions.append(f"取款: -{amount}")
            return True
        return False

    def get_statement(self):
        return self.__transactions

练习题

  1. 实现一个简单的图书管理系统,包含图书和用户类
  2. 设计一个宠物商店系统,使用继承实现不同类型的宠物
  3. 实现一个简单的学生成绩管理系统

小结

  • 理解面向对象的基本概念
  • 掌握类和对象的创建和使用
  • 理解封装、继承和多态
  • 掌握Python特有的魔术方法
  • 了解基本的设计模式
  • 能够实现简单的面向对象程序