💡 课堂例题
第 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}元')