周末总结
day01
-
玩家摇两颗色子,如果第一次摇出了7点或11点,玩家胜;如果摇出了2点、3点、12点,庄家胜;
如果摇出了其他的点数,游戏继续,玩家重新摇色子;如果玩家摇出了第一次摇的点数,玩家胜;
如果玩家摇出了7点,庄家胜;如果玩家摇出其他点数,游戏继续,玩家重新摇色子,直到分出胜负。游戏开始之前,玩家有1000元的初始资金,玩家可以下注,赢了获得下注的金额,输了就扣除下注的金额,
游戏结束的条件是玩家把钱输光。
import random
money = 1000
while money > 0:
print(f'玩家的总资产{money}')
duzhu = 0
# 这里赌注不能写在外面,不然循环会一直执行下去,不会重新下注
while duzhu <= 0 or duzhu > money:
try:
duzhu = int(input('请下注:'))
except ValueError:
print('请重新下注:')
## 前面的注码是0,所以会执行while循环,正确不循环,错误会进入循环
## except 可以避免错误输出在屏幕上
first_point = random.randrange(1, 7) + random.randrange(1, 7)
# print(f'摇出的色子{first_point}')
if first_point in (7, 11):
print('玩家胜')
money += duzhu
elif first_point in (2, 3, 12):
print('庄家胜')
money -= duzhu
else:
while True:
curr_point = random.randrange(1, 7) + random.randrange(1, 7)
# print(f'摇出的色子{curr_point}')
if curr_point == first_point:
print('玩家胜')
money += duzhu
break #代表这轮游戏结束,重新开始
elif curr_point == 7:
print('庄家胜')
money -= duzhu
break
print('游戏结束,玩家的钱已被坑完')
- 红色球01-33,选择不重复的6个球,按从小到大排列
蓝色球01-16,选择一个球,跟在红色球的后面
import random
red_balls = [i for i in range(1, 34)]
blue_balls = [i for i in range(1, 16)]
select_balls = random.sample(red_balls,6)
# sample 生成列表
select_balls.sort()
blue_ball = random.sample(blue_balls,1)
# select_balls.append(blue_ball)
# append()是以元素什么新式增加就以什么形式添加列表
# select_balls.extend(blue_ball)
# 是把元素去掉列表形式添加到列表
select_balls += random.choices(blue_balls,k = 1)
for ball in select_balls:
print(f'{ball:0>2d}',end=' ')
注意的知识点:列表的合并
append()
是以元素什么新式增加就以什么形式添加列表。extend()
是把元素去掉列表形式添加到列表。list1
+ =list2
是把列表2元素添加到列表1中,并不会以列表的形加入。sort()
不能赋值,但sorted()
函数可以进行赋值操作。randon.sample(对象,取值数)
,可以进行赋值操作,与其一样还有choices
(生成列表)和choice
(不生成列表)
函数
函数的作用
不知大家是否注意到,上面的代码中我们做了三次求阶乘,虽然m
、n
、m - n
的值各不相同,但是三段代码并没有实质性的区别,属于重复代码。世界级的编程大师Martin Fowler先生曾经说过:“代码有很多种坏味道,重复是最坏的一种!”。要写出高质量的代码首先要解决的就是重复代码的问题。对于上面的代码来说,我们可以将计算阶乘的功能封装到一个称为“函数”的代码块中,在需要计算阶乘的地方,我们只需要“调用函数”就可以了。
定义函数
数学上的函数通常形如y = f(x)
或者z = g(x, y)
这样的形式,在y = f(x)
中,f
是函数的名字,x
是函数的自变量,y
是函数的因变量;而在z = g(x, y)
中,g
是函数名,x
和y
是函数的自变量,z
是函数的因变量。Python中的函数跟这个结构是一致的,每个函数都有自己的名字、自变量和因变量。我们通常把Python中函数的自变量称为函数的参数,而因变量称为函数的返回值。
自变量(argument) ——》 参数(parameter)
因变量 ——》 返回值(return value)
在Python中可以使用def
关键字来定义函数,和变量一样每个函数也应该有一个漂亮的名字,命名规则跟变量的命名规则是一致的(赶紧想一想我们之前讲过的变量的命名规则)。在函数名后面的圆括号中可以放置传递给函数的参数,就是我们刚才说到的函数的自变量,而函数执行完成后我们会通过return
关键字来返回函数的执行结果,就是我们刚才说的函数的因变量。一个函数要执行的代码块(要做的事情)也是通过缩进的方式来表示的,跟之前分支和循环结构的代码块是一样的。大家不要忘了def
那一行的最后面还有一个:
,那是在英文输入法状态下输入的冒号。
我们可以通过函数对上面的代码进行重构。所谓重构,是在不影响代码执行结果的前提下对代码的结构进行调整。
m = int(input('请输入一个整数'))
n = int(input('请输入一个整数'))
def fac(num):
"""
求数的阶乘
:param num:
:return:
"""
result = 1
for i in range(2, num + 1):
result *= i
return result
print(fac(m)//fac(n)//fac(m - n))
全局变量和局部变量
- 全局变量(没有写在任何函数里面的变量):起作用的是全局。
- 局部变量(定义在函数内部的变量):起作用的部分仅仅是定义函数内。
两个变量相互没有影响,各自不影响。
Python程序中搜索一个变量是按照 LEGB 顺序进行搜索的
Local(局部作用域) —> Embeded(嵌套作用域) —> Global(全局作用域)
—> Built-in(内置作用域) —> NameError: name … not defined
global —> 声明使用全局变量或者定义一个局部变量将其放到全局作用域
nonlocal —> 声明使用嵌套作用域的变量(不使用局部变量)
x = 100
# 全局变量
def foo():
# 如果我不想在函数foo中定义局部变量x,想直接使用全局变量x,应该怎么做???
# global x 这个x是来自于全局 把全局变量变成200
# 所以加上global输出 300(嵌套的的300) 200 200
x = 200
def bar():
# 如果我不想在函数bar中定义局部变量x,想直接使用嵌套作用域中的x,应该怎么做???
# nonlocal x
# 不能与global一起用
# 加上后,外面局部变量的x不会用,用嵌套的嵌套里的x
# 300 300 100
x = 300
print(x)
bar()
print(x)
foo()
print(x)
# 没有g...和n...时:输出 300 200 100
递归函数
feibolaqi数列
1, 1, 2, 3, 5, 8, 13,21, 34, 55, 。。。。
f(n) =f(n-1) + f(n -2)
-
递归函数的两个要点:
1.递归公式(第n次跟第n-1次的关系)
2.收敛条件(什么时候停止递归的调用)3.自己调用自己
# 省时间和省空间不可以二者兼得
def fib(n, temp={}):
if n in (1, 2):
return 1
if n not in temp:
temp[n] = fib(n - 1) + fib(n - 2)
# 该if是减少递归循环的时间
return temp[n]
if __name__ == '__main__':
for i in range(1,21):
print(i,fib(i))
注释
在自己写的函数中需要加上文档注释:
def ...():
"""
文档注释
"""
原因:
- 因为自己写的函数不一定是自己调用,可能其他人也会调用这个函数,如果没有文档注释,别人就无法理解这个函数在做什么,也不知道如何使用这个函数。
- 加了之后,调用函数时,可以看到函数的文档注释(使用说
PC中物理重新构造函数的方法
选中需要构建的函数代码,右键选择Refactor
,在选择Extract Method…
提示框出来后,加上在第一栏写上函数的名称就点🆗。
day02
一、写一个实现生成指定长度的随机验证码(有数字和英文字母构成)的函数
Snake case —> Python标识符命名风格
import random
import string
def get_captcha_code(length: int = 4) -> str:
"""生成随机验证码
:param length: 验证码的长度
:return: 随机验证码字符串
"""
selected_chars = random.choices(string.digits + string.ascii_letters, k=length)
return ''.join(selected_chars)
for _ in range(10):
print(get_captcha_code())
二、写一个函数判断一个正整数是不是质数。
def is_prime(num: int) -> bool:
"""判断一个正整数是不是质数
:param num: 正整数
:return: 如果是质数返回True,否则返回False
"""
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
return False
return num != 1
for n in range(2, 100):
if is_prime(n):
print(n, end=' ')
三 、用函数实现求两个数的最大公约数和最小公倍数。
写程序的终极原则:高内聚,低耦合 —> high cohesion low coupling
设计函数最为重要的原则:单一职责原则(一个函数只做好一件事情)—> 高度内聚
GCD —> 欧几里得算法(辗转求余数法)
def gcd(x: int, y: int) -> int:
"""求最大公约数"""
while y % x != 0:
x, y = y % x, x
return x
def lcm(x: int, y: int) -> int:
"""求最小公倍数"""
return x * y // gcd(x, y)
print(gcd(27, 15))
print(lcm(27, 15))
四、设计获得样本数据描述性统计信息的函数
集中趋势:均值、中位数、众数
离散趋势:极差、方差、标准差
import math
import random
def ptp(data):
"""求极差(全距)"""
return max(data) - min(data)
def average(data):
"""求均值"""
return sum(data) / len(data)
def variance(data):
"""求方差"""
x_bar = average(data)
temp = [(num - x_bar) ** 2 for num in data]
return sum(temp) / (len(temp) - 1)
def standard_deviation(data):
"""求标准差"""
return math.sqrt(variance(data))
def median(data):
"""找中位数"""
temp, size = sorted(data), len(data)
if size % 2 != 0:
return temp[size // 2]
else:
return average(temp[size // 2 - 1: size // 2 + 1])
# __name__是一个隐藏变量,它代表了当前模块(文件)的名字
# 如果直接通过python解释器运行importhomework04.py这个文件,这个 __name__的名字是__main__’
# 当其他文件模块导入importhomework04时,就不会时‘__main__’这个名字,是importhomework04
if __name__ == '__main__':
nums = [random.randrange(1, 100) for _ in range(8)]
print(nums)
print(f'均值: {average(nums)}')
print(f'中位数: {median(nums)}')
print(f'极差: {ptp(nums)}')
print(f'方差: {variance(nums)}')
print(f'标准差: {standard_deviation(nums)}')
day03
一、关键字
在设计函数的时候,函数的参数个数是暂时无法确定
argument
位置参数:
关键字参数:
关键字参数一定在位子参数的后面
’*‘前面时位置参数,后面是关键字参数,必须’=‘赋值。
参数可以给出默认值,当调用函数未给出默认值时,不会替换默认值,而会调用定义时的默认值
def calc(*args, **kwargs):
total = 0
result = 0
for arg in args:
if type(arg) in (int,float):
total += arg
for value in kwargs.values():
if type(value) in (int,float):
result += value
return total,result
print(calc()) # 0
print(calc(1, 2, 3)) # 6
print(calc(a=1, b=2, c=3)) # 6
print(calc(1, 2, c=3, d=4)) # 10
二、一阶函数和高阶函数
python中的函数的一等函数:
1.函数可以作为函数的参数
2函数可以作为函数的返回值,
3.函数可以赋值给变量
如果把函数作为函数的参数或返回值,这种玩法通常称之为高阶函数
通常使用高阶函数
def calc(*args, init_value, op, **kwargs):
total = init_value
for arg in args:
if type(arg) in (int, float):
total = op(total, arg)
for value in kwargs.values():
if type(value) in (int, float):
total = op(total, value)
return total
def add(x, y):
return x + y
def mul(x, y):
return x * y
def sub(x, y):
return x - y
print(calc( 1, 2, 3,init_value=0,op=operator.add, x=4, y=5)) # 15
print(calc(1, 2, y=4, z=5,init_value=1, op=mul,))
设计函数的时候,一定要注意函数的无副作用性(调用函数不能影响传入无副作用性),不会改变调用者
lambda(匿名函数)
在使用高阶函数的时候,如果作为参数或者返回值的函数本身非常简单,一行代码就能够完成,那么我们可以使用Lambda函数来表示。Python中的Lambda函数是没有的名字函数,所以很多人也把它叫做匿名函数,匿名函数只能有一行代码,代码中的表达式产生的运算结果就是这个匿名函数的返回值。
import operator, functools
#一行代码定义求阶乘的函数
fac = lambda num: functools.reduce(operator.mul, range(1, num + 1), 1)
#一行代码定义判断素数的函数
is_prime = lambda x: x > 1 and all(map(lambda f: x % f, range(2, int(x ** 0.5) + 1)))
#调用Lambda函数
print(fac(10)) # 3628800
print(is_prime(9)) # False
第二个代码的op=()和lambda函数一起用。
day04
一.面向对象编程
指令式编程 ——> 面向过程(函数)编程 ——> 程序比较简单的时候没有任何毛病
编程范式(程序设计方法论):面向对象编程 / 函数式编程
对象:对象是可以接收消息的实体,面向对象编程就是通过给对象发消息达到解决问题的目标
-
对象 = 数据 + 函数(方法) —–> 对象将数据和操作数据的函数从逻辑上变成一个整体
-
一起皆为对象
-
对象都有属性和行为
-
每个对象都是独一无二
-
对象一定属于某个类
类:将有共同特征(静态特征和动态特征)的对象的共同特征抽取出来之后得到的一个抽象概念。
简单的说,类是对象的蓝图(模板),有了类才能够创建出这种类型的对象。
面向对象编程:
-
-
定义类 —> 类的命名使用驼峰命名法(每个单词首字母大写)
- 数据抽象:找到和对象相关的静态特征(属性)—> 找名词
- 行为抽象:找到和对象相关的动态特征(方法)—> 找动词
- 造对象
- 发消息
# 第一步:定义类
class Student:
"""学生"""
# 数据抽象(属性)
def __init__(self, name, age):
self.name = name
self.age = age
# 行为抽象(方法)
def eat(self):
"""吃饭"""
print(f'{self.name}正在吃饭.')
def study(self, course_name):
"""学习"""
print(f'{self.name}正在学习{course_name}.')
def play(self, game_name):
"""玩"""
print(f'{self.name}正在玩{game_name}.')
from example01 import Student
# 第二步:创建对象 ---> 构造器语法 ---> 类名(..., ...)
stu1 = Student('王大锤', 15)
stu2 = Student('骆昊', 41)
# Student.study(stu1, 'Python程序设计')
# 第三步:给对象发消息(调用对象的方法)
stu1.study('Python程序设计')
stu1.eat()
stu2.play('斗地主')
stu1.age = 20
二.用面向对象编程思想解决现实问题
给定一个圆环水池,围墙每米造价38.5元,过道宽3米,每米造价58.5元。
init
—> initialize —> 初始化
1.抽象:找到问题的本质,核心对象:大圆,小圆(定义圆类,创建两个圆的对象,通过给对象发消息求解问题)
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def perimeter(self):
return 2 * math.pi * self.radius
def area(self):
return math.pi * self.radius ** 2
if __name__ == '__main__':
r = float(input('请输入游泳池的半径: '))
c1, c2 = Circle(r), Circle(r + 3)
fence_price = c2.perimeter() * 38.5
aisle_price = (c2.area() - c1.area()) * 58.5
print(f'围墙的造价: {fence_price:.2f}元')
print(f'过道的造价: {aisle_price:.2f}元')
print(f'总计: {fence_price + aisle_price: .2f}元')
创建一个时钟对象(可以显示时/分/秒),让它运转起来(走字)。
-
名词:
- 时钟 —> 类
-
时、分、秒 —> 属性
动词: 显示、走字
class Clock:
"""时钟"""
def __init__(self, hour=0, minute=0, second=0):
"""数据抽象:属性"""
self.hour = hour
self.minute = minute
self.second = second
def show(self):
"""显示时间"""
return f'{self.hour:0>2d}:{self.minute:0>2d}:{self.second:0>2d}'
def run(self):
"""走字"""
self.second += 1
if self.second == 60:
self.second = 0
self.minute += 1
if self.minute == 60:
self.minute = 0
self.hour += 1
if self.hour == 24:
self.hour = 0
if __name__ == '__main__':
clock = Clock(23, 59, 58)
while True:
os.system('cls')
print(clock.show())
time.sleep(1)
clock.run()
注意知识点:
os
:需要import导入,可以在Terminal显示,苹果端的命令时os.system('clear')
,在安卓端时cls
。0>2d
:输出格式化,占两位,没有数值以0来填补。- 在定义属性是可以定义默认值。
三.定义描述三角形的类,提供计算周长和面积的方法
我们在类里面写的函数,通常称之为方法,它们基本上都是发给对象的消息。
但是有的时候,我们的消息并不想发给对象,而是希望发给这个类(类本身也是一个对象),
这个时候,我们可以使用静态方法或类方法。
静态方法 - 发给类的消息 —> @staticmethod
—> 装饰器
类方法 - 发给类的消息 —>@classmethod
—> 装饰器 —> 第一个参数(cls
)是接收消息的类
class Triangle:
"""三角形"""
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
# @classmethod
# def is_valid(cls, a, b, c):
# return a + b > c and b + c > a and a + c > b
@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):
half = self.perimeter() / 2
return (half * (half - self.a) * (half - self.b) * (half - self.c)) ** 0.5
if __name__ == '__main__':
# 调用静态方法,判断三条边能否构成三角形
if Triangle.is_valid(3, 4, 5):
t = Triangle(3, 4, 5)
print(t.perimeter())
print(t.area())
else:
print('无效的边长,无法构造三角形对象')
方法二
def __init__(self, a, b, c):
if not Triangle.is_valid(a, b, c):
raise ValueError('无效边长') # raise 引发错误
"""初始化方法"""
self.a = a
self.b = b
self.c = c
# @classmethod # (类方法)
# def is_valid(cls, a, b, c):
# """判断三条边长能否构成三角形(静态方法)"""
# return a + b > c and b + c > a and a + c > b
@staticmethod
# 静态方法(类方法) 写成Triangle.is_vaild()
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):
half = self.perimeter() / 2
return (half * (half - self.a) * (half - self.b) * (half - self.c))
if __name__ == '__main__':
try:
t = Triangle(1, 2, 3)
print(t.perimeter()) # 当代码发生异常,这下面的都不执行#但except没有捕获到异常,就不会执行except的代码
print(t.area())
except ValueError as err:
print(err)
注意点
- try … except …:通过
@staticmethod(静态方法)
和@classmethod (类方法)
来设计一个is_valid()来发给类一个方法,来判断是否能构成三角形,不能构成就用raise ValueError()
来引发异常,并用except来捕获。
day05
一、
写一个扑克游戏,四个玩家参与,先洗牌,把牌发给四个玩家的手术
- 牌
- 属性: 花色,点数
- 行为: 显示
- 扑克
- 属性:保存牌的列表
- 行为:洗牌,发牌
- 玩家
- 属性:名字(昵称), 保存玩家手牌的列表
- 行为:摸牌(把牌弄到自己的手上),整理手上的牌
通过这个例子,可以更加深入了解面向对象更多知识点
1.牌类:
class Card:
def __init__(self, suite, face):
self.suite = suite
self.face = face
def __str__(self):
return self.show()
# 没有这个方法就会出现默认地址
def show(self):
suites = {'S': '♠', 'H': '❤', 'C': '♣', 'D': '♦'}
faces = ['', 'A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
return f'{suites[self.suite]}{faces[self.face]}'
2.扑克类
注意:如果牌类和扑克类不在一个模块下,要导入该模块from…import…
class Poker:
"""扑克"""
def __init__(self):
# self.cards = []
# for suite in 'SHCD':
# for face in range(1, 14):
# card = Card(suite,face)
# self.cards.append(card)
self.cards = [Card(suite, face) for suite in 'SHCD' for face in range(1, 14)]
self.counter = 0
def shuffle(self):
"""洗牌"""
self.counter = 0
random.shuffle(self.cards)
def deal(self) -> Card:
"""发牌"""
card = self.cards[self.counter]
self.counter += 1
return card
def has_more(self) -> bool:
"""是否还有牌"""
return self.counter < len(self.cards)
3.玩家
from example02 import Poker
class Player:
"""玩家"""
def __init__(self, nickname):
self.nickname = nickname
self.cards = []
# 类里面的初始化
def get_one_card(self, card):
"""摸一张牌"""
self.cards.append(card)
def arrange(self):
"""整理手上的牌"""
self.cards.sort()
def show(self):
"""显示玩家手上的牌"""
print(self.nickname, end=':')
for card in self.cards:
print(card, end='')
print()
def main():
nicknames = ('喜羊羊', '美羊羊', '沸羊羊', '懒洋洋')
players = [Player(nickname) for nickname in nicknames]
poker = Poker()
poker.shuffle()
for _ in range(10):
for player in players:
card = poker.deal()
player.get_one_card(card)
# 显示四个玩家手上的牌
for player in players:
player.arrange()
player.show()
if __name__ == '__main__':
main()
-
sort(cmp=None, key=None, reverse=False)
-
sorted(iterable, cmp=None, key=None, reverse=False)
-
sort是容器的函数,用List的成员函数sort进行排序
-
sorted是Python的内建函数相同的参数,用built-in函数sorted进行排序
-
sorted(iterable,key=None,reverse=False),返回新的列表,对所有可迭代的对象均有效
-
sort(key=None,reverse=False) 就地改变列表 reverse:True反序;False 正序
修改遍历:
-
魔术方法(魔法方法)—> 有特殊用途和意义的方法
- init —> 初始化方法,在调用构造器语法创建对象的时候会被自动调用
- str —> 获得对象的字符串表示,在调用print函数输出对象时会被自动调用
-
repr —> 获得对象的字符串表示,把对象放到容器中调用print输出时会自动调用
—> representation - lt —> 在使用 < 运算符比较两个对象大小时会自动调用
如果一个变量的取值只有有限个选项,可以考虑使用枚举类型。
Python中没有定义枚举类型的语法,但是可以通过继承Enum类来实现枚举类型。
结论1:枚举类型是定义符号常量的最佳选择!!!
结论2:符号常量(有意义的名字)总是优于字面常量!!!
from enum import Enum
class Suite(Enum):
SPADR, HEART, CLUB, DIAMOD = range(4)
for suite in Suite:
print(suite,suite.value)
运用知识点修改后
from enum import Enum
# 枚举类型
class Suite(Enum):
SPADE, HEART, CLUB, DIAMOND = range(4)
class Card:
"""牌"""
def __init__(self, suite, face):
self.suite = suite
self.face = face
def __str__(self):
return self.show()
def __repr__(self):
return self.show()
def __lt__(self, other):
return self.show()
def __lt__(self, other):
if self.suite == other.suite:
return self.face < other.face
return self.suite.value < other.suite.value
def show(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 __init__(self):
self.cards = [Card(suite, face)
for suite in Suite
for face in range(1, 14)]
self.counter = 0
其余代码一致,加上Enum函数能很好的避开符号之类的排序
二、
如果要限制一个类的对象只能拥有某些属性,可以在类中使用__slots__魔法属性
class Student:
slots = (‘name’, ‘gender’)
继承:对已有的类进行扩展创建出新的类,这个过程就叫继承。
提供继承信息的类叫做父类(超类、基类),得到继承信息的类称为子类(派生类)。
继承是实现代码复用的一种手段,但是千万不要滥用继承。
继承是一种is-a关系。
a student is a person.
a teacher is a person.
a programmer is a person.
子类直接从父类继承公共的属性和行为,再添加自己特有的属性和行为,
所以子类一定是比父类更强大的,任何时候都可以用子类对象去替代父类对象。
Python中的继承允许多重继承,一个类可以有一个或多个父类。
如果不是必须使用多重继承的场景下,请尽量使用单一继承。
class Person:
"""人"""
def __init__(self, name, gender):
self.name = name
self.gender = gender
def eat(self):
"""吃饭"""
print(f'{self.name}正在吃饭.')
def play(self, game_name):
"""玩"""
print(f'{self.name}正在玩{game_name}.')
def introduce(self):
"""自我介绍"""
sex = "男" if self.gender else "女"
print(f'我叫{self.name}, 是一个{sex}人.')
class Student(Person):
"""学生"""
def __init__(self, name, gender, grade):
# 继承父类的属性
super().__init__(name, gender)
# 子类可以自己进行修改
self.grade = grade
def study(self, course_name):
"""学习"""
print(f'{self.name}正在学习{course_name}.')
class Teacher(Person):
"""老师"""
def __init__(self, name, gender, title):
super().__init__(name, gender)
self.title = title
def teach(self, course_name):
"""教课"""
print(f'{self.name}{self.title}正在讲授{course_name}.')
class Programmer(Person):
"""程序员"""
def write_code(self, programming_language):
"""写代码"""
print(f'{self.name}正在用{programming_language}写代码.')
stu = Student('王大锤', True, '五年级')
stu.study('语文')
stu.play('王者荣耀')
stu.eat()
stu.introduce()
teacher = Teacher('骆昊', True, '教授')
teacher.eat()
teacher.play('斗地主')
teacher.teach('Python程序设计')
teacher.introduce()
programmer = Programmer('白元芳', True)
programmer.eat()
programmer.play('吃鸡')
programmer.write_code('Python')
三、
两个类之间有哪些可能的关系???
-
~ is-a关系:继承 —> 从一个类派生出另一个类
a student is a person.
a teacher is a person. -
~ has-a关系:关联 —> 把一个类的对象作为另外一个类的对象的属性
a person has an identity card.
a car has an engine.-
(普通)关联强关联:
整体和部分的关联,聚合和合成。
-
use-a关系:依赖 —> 一个类的对象作为另外一个类的方法的参数或返回值
a person use a vehicle.
-
class Vehicle:
"""交通工具"""
pass
# 子类继承父类:继承
class Horse(Vehicle):
"""马"""
pass
# 继承
class Motobike(Vehicle):
"""摩托车"""
# 关联:强关联
def __init__(self):
self.engine = Engine()
class Engine:
"""引擎"""
pass
class Follower:
"""徒弟"""
def __init__(self, name):
self.name = name
def do_work(self):
pass
class MrTang:
"""唐僧"""
# 拥有关系 --has关系:合成
def __init__(self):
self.followers = [Follower('孙悟空'), Follower('猪悟能'), Follower('沙悟净')]
def drive(self, vehicle):
"""驾驶"""
pass
# has 关系,强关系,聚类
四、
工资(月薪)结算系统
-
面向对象编程的四大支柱:
- 抽象(abstraction):提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象)。
-
封装(encapsulation):把数据和操作数据的函数从逻辑上组装成一个整体(对象)。
—> 隐藏实现细节,暴露简单的调用接口。 - 继承(inheritance):扩展已有的类创建新类,实现对已有类的代码复用。
-
多态(polymorphism):给不同的对象发出同样的消息,不同的对象执行了不同的行为。
—> 方法重写:子类对父类已有的方法,重新给出自己的实现版本
三类员工:
- 部门经理:固定月薪,15000元
- 程序员:计时结算月薪,每小时200元
- 销售员:底薪+提成,底薪1800元,销售额5%提成
录入员工信息,自动结算月薪
子类对父类已有的方法,重新给出自己的实现版本,这个过程叫做方法重写(override)。
在重写方法的过程中,不同的子类可以对父类的同一个方法给出不同的实现版本,那么该方法在运行时就会表现出多态行为。
“”"
from abc import abstractmethod
class Employee:
def __init__(self, no, name):
self.no = no
self.name = name
@abstractmethod
def get_salary(self):
pass
class Manager(Employee):
def get_salary(self):
return 15000
class Programmer(Employee):
def __init__(self, no, name):
super().__init__(no, name)
self.working_hour = 0
def get_salary(self):
return 200 * self.working_hour
class Salesman(Employee):
def __init__(self, no, name):
super().__init__(no, name)
self.sales = 0
def get_salary(self):
return 1800 + 0.05 * self.sales
def main():
emps = [
Manager(1122, '刘备'), Programmer(2233, '诸葛亮'),
Salesman(3344, '关羽'), Salesman(4455, '张飞'),
Programmer(5566, '庞统'), Salesman(6677, '马超')
]
for emp in emps:
if type(emp) == Programmer:
# 这里需要type()函数
emp.working_hour = int(input(f'请输入{emp.name}本月工作时长: '))
elif type(emp) == Salesman:
emp.sales = float(input(f'请输入{emp.name}本月销售额: '))
print(f'{emp.name}本月工资: {emp.get_salary()}元')
if __name__ == '__main__':
main()
知识点:
- 抽象(abstraction):提取共性(定义类就是一个抽象过程,需要做数据抽象和行为抽象),需要
from abc import abstractmethod
来导入 - 静态方法 - 发给类的消息 —> @staticmethod —> 装饰器
- 类方法 - 发给类的消息 —> @classmethod —> 装饰器 —> 第一个参数(cls)是接收消息的类