💡 课堂例题

第 7 章 · 面向对象编程 · 共 10 题 · 代码均取自课堂讲稿

1
定义类与创建对象
使用 class 关键字定义 Student 类,包含 study 和 play 两个方法。通过构造器语法创建对象,理解对象在内存中的地址。
python
class Student:

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

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


stu1 = Student()
stu2 = Student()
print(stu1)    # <__main__.Student object at 0x10ad5ac50>
print(stu2)    # <__main__.Student object at 0x10ad5acd0>
print(hex(id(stu1)), hex(id(stu2)))    # 0x10ad5ac50 0x10ad5acd0

# 两种调用方式
Student.study(stu1, 'Python程序设计')    # 学生正在学习Python程序设计.
stu1.study('Python程序设计')             # 学生正在学习Python程序设计.
Student.play(stu2)                      # 学生正在玩游戏.
stu2.play()                             # 学生正在玩游戏.
2
初始化方法 __init__
为学生对象添加 name 和 age 属性,通过 __init__ 方法完成初始化。理解 self 的作用——代表当前对象本身。
python
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('骆昊', 44)
stu2 = Student('王大锤', 25)
stu1.study('Python程序设计')    # 骆昊正在学习Python程序设计.
stu2.play()                    # 王大锤正在玩游戏.
3
案例:Clock 数字时钟
定义时钟类,包含 __init__ 初始化、run 走字、show 显示时间三个方法。演示面向对象的"三步走":定义类→创建对象→发消息。
python
import time


class Clock:
    """数字时钟"""

    def __init__(self, hour=0, minute=0, second=0):
        self.hour = hour
        self.min = minute
        self.sec = second

    def run(self):
        """走字"""
        self.sec += 1
        if self.sec == 60:
            self.sec = 0
            self.min += 1
            if self.min == 60:
                self.min = 0
                self.hour += 1
                if self.hour == 24:
                    self.hour = 0

    def show(self):
        """显示时间"""
        return f'{self.hour:0>2d}:{self.min:0>2d}:{self.sec:0>2d}'


clock = Clock(23, 59, 58)
while True:
    print(clock.show())
    time.sleep(1)
    clock.run()
4
案例:Point 点类
定义平面上的点类,包含 __init__、distance_to(计算两点距离)、__str__(魔法方法,控制对象的字符串表示)。
python
class Point:
    """平面上的点"""

    def __init__(self, x=0, y=0):
        self.x, self.y = x, y

    def distance_to(self, other):
        """计算与另一个点的距离"""
        dx = self.x - other.x
        dy = self.y - other.y
        return (dx * dx + dy * dy) ** 0.5

    def __str__(self):
        return f'({self.x}, {self.y})'


p1 = Point(3, 5)
p2 = Point(6, 9)
print(p1)  # (3, 5)
print(p2)  # (6, 9)
print(p1.distance_to(p2))  # 5.0
5
可见性:私有属性
通过 __ 前缀定义私有属性,类外部无法直接访问(会引发 AttributeError)。演示 __slots__ 限制动态属性添加。
python
# --- 私有属性 ---
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('王大锤', 20)
stu.study('Python程序设计')
# print(stu.__name)  # AttributeError!


# --- __slots__ 限制动态属性 ---
class Student2:
    __slots__ = ('name', 'age')

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


stu2 = Student2('王大锤', 20)
# stu2.sex = '男'  # AttributeError!
6
静态方法 @staticmethod 与类方法 @classmethod
三角形类演示静态方法 is_valid(无需创建对象即可调用),以及对象方法 perimeter 和 area。
python
class Triangle(object):
    """三角形"""

    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    @staticmethod
    def is_valid(a, b, c):
        """判断三条边长能否构成三角形(静态方法)"""
        return a + b > c and b + c > a and a + c > b

    def perimeter(self):
        """计算周长"""
        return self.a + self.b + self.c

    def area(self):
        """计算面积"""
        p = self.perimeter() / 2
        return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5


# 静态方法通过类名直接调用
print(Triangle.is_valid(3, 4, 5))  # True
print(Triangle.is_valid(1, 2, 5))  # False

t = Triangle(3, 4, 5)
print(f'周长: {t.perimeter()}')      # 12
print(f'面积: {t.area():.1f}')       # 6.0
7
@property 装饰器
使用 @property 将 perimeter 和 area 方法转为属性访问方式,调用时不需要加括号。
python
class Triangle(object):
    """三角形"""

    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    @staticmethod
    def is_valid(a, b, c):
        """判断三条边长能否构成三角形(静态方法)"""
        return a + b > c and b + c > a and a + c > b

    @property
    def perimeter(self):
        """计算周长"""
        return self.a + self.b + self.c

    @property
    def area(self):
        """计算面积"""
        p = self.perimeter / 2
        return (p * (p - self.a) * (p - self.b) * (p - self.c)) ** 0.5


t = Triangle(3, 4, 5)
print(f'周长: {t.perimeter}')   # 无需括号
print(f'面积: {t.area}')        # 无需括号
8
继承:Person → Student / Teacher
定义 Person 父类(name、age、eat、sleep),子类 Student 和 Teacher 通过 super().__init__() 继承父类初始化,并添加各自特有方法。
python
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)
tea1 = Teacher('武则天', 35, '副教授')
stu1.eat()                       # 白元芳正在吃饭.
stu1.study('Python程序设计')      # 白元芳正在学习Python程序设计.
tea1.teach('Python程序设计')      # 武则天副教授正在讲授Python程序设计.
9
综合案例:扑克游戏(枚举 + __lt__ 运算符重载 + @property)
定义 Suite 枚举、Card 牌类(含 __repr__ 和 __lt__ 运算符重载)、Poker 扑克类(shuffle/deal/has_next)、Player 玩家类。52张牌发4人各13张并按花色点数排序。
python
from enum import Enum
import random


class Suite(Enum):
    """花色(枚举)"""
    SPADE, HEART, CLUB, DIAMOND = range(4)


class Card:
    """牌"""

    def __init__(self, suite, face):
        self.suite = suite
        self.face = face

    def __repr__(self):
        suites = '♠♥♣♦'
        faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
        return f'{suites[self.suite.value]}{faces[self.face]}'

    def __lt__(self, other):
        if self.suite == other.suite:
            return self.face < other.face
        return self.suite.value < other.suite.value


class Poker:
    """扑克"""

    def __init__(self):
        self.cards = [Card(suite, face)
                      for suite in Suite
                      for face in range(1, 14)]
        self.current = 0

    def shuffle(self):
        """洗牌"""
        self.current = 0
        random.shuffle(self.cards)

    def deal(self):
        """发牌"""
        card = self.cards[self.current]
        self.current += 1
        return card

    @property
    def has_next(self):
        """还有没有牌可以发"""
        return self.current < len(self.cards)


class Player:
    """玩家"""

    def __init__(self, name):
        self.name = name
        self.cards = []

    def get_one(self, card):
        """摸牌"""
        self.cards.append(card)

    def arrange(self):
        """整理手上的牌"""
        self.cards.sort()


# 发牌
poker = Poker()
poker.shuffle()
players = [Player('东邪'), Player('西毒'), Player('南帝'), Player('北丐')]
for _ in range(13):
    for player in players:
        player.get_one(poker.deal())
for player in players:
    player.arrange()
    print(f'{player.name}: ', end='')
    print(player.cards)
10
综合案例:工资结算系统(抽象类 + 多态)
定义 Employee 抽象类(ABCMeta + @abstractmethod),Manager/Programmer/Salesman 三个子类各实现 get_salary 多态。使用 isinstance 判断类型并批量结算。
python
from abc import ABCMeta, abstractmethod


class Employee(metaclass=ABCMeta):
    """员工"""

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

    @abstractmethod
    def get_salary(self):
        """结算月薪"""
        pass


class Manager(Employee):
    """部门经理"""

    def get_salary(self):
        return 15000.0


class Programmer(Employee):
    """程序员"""

    def __init__(self, name, working_hour=0):
        super().__init__(name)
        self.working_hour = working_hour

    def get_salary(self):
        return 200 * self.working_hour


class Salesman(Employee):
    """销售员"""

    def __init__(self, name, sales=0):
        super().__init__(name)
        self.sales = sales

    def get_salary(self):
        return 1800 + self.sales * 0.05


emps = [Manager('刘备'), Programmer('诸葛亮'), Manager('曹操'),
        Programmer('荀彧'), Salesman('张辽')]
for emp in emps:
    if isinstance(emp, Programmer):
        emp.working_hour = int(input(f'请输入{emp.name}本月工作时间: '))
    elif isinstance(emp, Salesman):
        emp.sales = float(input(f'请输入{emp.name}本月销售额: '))
    print(f'{emp.name}本月工资为: ¥{emp.get_salary():.2f}元')