'''实参'''
def fun_param(a, b, c, d):
print(a), print(b), print(c), print(d)
# 位置实参:实参与形参的位置依次对应
fun_param(1, 2, 3, 4)
# 关键字实参:实参与形参根据名称进行对应
fun_param(b=1, d=2, c=3, a=4)
# 序列实参(属于位置实参):星号将序列拆分后按位置与形参进行对应
fun_param(*['a', 'b', 'c', 'd'])
# 字典实参(属于关键字实参):双星号将字典拆分后按名称与形参进行对应
fun_param(**{'a': 1, 'c': 3, 'd': 4, 'b': 2})
'''形参'''
# 缺省/默认参数:如果实参不提供,可以使用默认值
def fun_01(a=0, b=0, c=0, d=None):
pass
# 关键字实参+缺省形参:调用者可以随意传递参数
fun_01(b=1, d=2)
# 位置形参
def fun_02(a, b, c, d):
pass
# 星号元组形参:*将所有实参合并为一个元组. 作用:让实参个数无限制
def fun03(*args):
pass
# 命名关键字形参:在星号元组形参以后的位置形参
def fun04(a, *args, b): # 参数b是命名关键字形参,其对应的实参必须使用关键字实参
pass
# 双星号字典形参:实参可以传递数量无限的关键字实参;将实参合并为字典
def fun05(**kwargs):
pass
2. 可变与不可变类型在传参时的区别
2.1常见不可变类型参数
- 数值型,布尔型,None,字符串,元组,固定集合…
- 列表,字典,可变集合…
- 不可变类型数据传参时,函数内部不会改变原数据的值
- 可变类型数据传参时,函数内部改变原数据
def immutable_type(a):
a = 100
num_ = 1
immutable_type(num_) # 调用fun01,传入不可变类型参数num_
print(num_) # num_ = 1
def variable_type(b):
b[0] = 100
list_ = [1]
variable_type(list_) # 调用fun02,传入可变类型参数list_
print(list_[0]) # 打印list_[0],list_[0] = 100
P2 面向对象
1. 封装
- 数据角度讲: 将一些基本数据类型复合成一个自定义类型
- 行为角度讲:向类外提供必要的功能,隐藏实现的细节
- 设计角度讲
– 分而治之:将一个大的需求分解为许多类,每个类处理一个独立的功能
– 变则疏之:变化的地方独立封装,避免影响其他类
– 高内聚:类中各个方法都在完成一项任务(单一职责的类)
– 低耦合:类与类的关联性上与依赖度要低(每个类独立)
class Wife:
def __init__(self, name, age):
self.name = name
# self.__age = age # 实际将变量名改为:_类名__age
self.set_age(age)
def get_age(self):
return self.__age
def set_age(self, value):
if 21 <= value <= 31:
self.__age = value
else:
raise ValueError('我不要')
w01 = Wife('铁锤公主', 27)
w01.set_age(221)
print(w01.get_age())
1.1.2 使用property封装变量_过渡形式
class Wife:
def __init__(self, name, age):
self.name = name
self.age = age
def get_age(self):
return self.__age
def set_age(self, value):
if 21 <= value <= 31:
self.__age = value
else:
raise ValueError('我不要')
# property对象拦截对age变量的读写操作
age = property(get_age, set_age)
w01 = Wife('铁锤公主', 27)
w01.age = 22
print(w01.age)
1.1.3 使用property封装变量
class Wife:
def __init__(self, name, age):
self.name = name
self.age = age
@property # 创建property对象,只负责拦截读取操作
def age(self):
return self.__age
@age.setter # 只负责拦截写入操作
def age(self, value):
if 21 <= value <= 31:
self.__age = value
else:
raise ValueError('我不要')
w01 = Wife('铁锤公主', 27)
w01.age = 22
print(w01.age)
说明:
- 公开的实例变量,缺少逻辑验证。私有的实例变量与两个公开的方法相结合,又使调用者的操作略显复杂,而属性可以将两个方法的使用方式像操作变量一样方便。
- 限定一个类创建的对象只能有固定的实例变量
class Student:
__slots__ = ("name", "__age")
def __init__(self, name, age):
self.name = name
self.age = age
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
self.__age = value
1.2.3 备注
- 含有__slots__属性的类所创建的对象没有__dict__属性
备注: __ dict __:是python内置的属性,用来存储对象的实例变量 - 优点:防止用户因错写属性的名称而发生程序错误
- 缺点:丧失了动态语言可以在运行时为对象添加变量的灵活性
'''学生管理系统'''
class StudentModel:
'''
学生模型类
'''
def __init__(self, name="", age=0, score=0, id=0):
'''
创建学生对象
:param name: 姓名,str类型
:param age: 年龄,int类型
:param score: 成绩,float类型
:param id: 编号(该学生对象的唯一标识)
'''
self.name = name
self.age = age
self.score = score
self.id = id
class StudentManagerController:
'''
学生管理控制器,负责业务逻辑处理.
'''
__init_id = 1000 # 类变量,表示初始编号
def __init__(self):
'''
初始化学生列表,用来添加学生对象
'''
self.__stu_list = []
@property
def stu_list(self):
'''
学生列表
:return: 存储学生对象的列表
'''
return self.__stu_list
def add_student(self, stu_info):
'''
添加学生
:param stu_info: 没有编号的学生信息
'''
stu_info.id = self.__generate_id()
self.__stu_list.append(stu_info)
def __generate_id(self):
'''
id生成器,每次调用该函数使类变量__init_id加1
:return: 数值加1后的类变量__init_id
'''
StudentManagerController.__init_id += 1
return StudentManagerController.__init_id
def remove_student(self, number):
'''
根据给定的编号移除学生信息
:param number: 给定的编号
:return: True:删除成功,False:删除失败
'''
for index in range(len(self.__stu_list)):
if self.__stu_list[index].id == number:
del self.__stu_list[index]
return True
return False
def update_student(self, number):
'''
根据指定编号修改学生信息
:param number: 给定的编号
:return: True:修改成功,False:修改失败
'''
for index in range(len(self.__stu_list)):
if self.__stu_list[index].id == number:
self.__stu_list[index].name = input("请输入学生姓名:n")
self.__stu_list[index].age = int(input("请输入学生年龄:n"))
self.__stu_list[index].score = int(input("请输入学生成绩:n"))
return True
return False
def order_by_score(self):
'''
根据学生成绩降序排列学生列表中的学生对象
'''
for i in range(len(self.__stu_list)):
for j in range(len(self.__stu_list) - 1 - i):
if self.__stu_list[j].score < self.__stu_list[j + 1].score:
temp_stu = self.__stu_list[j]
self.__stu_list[j] = self.__stu_list[j + 1]
self.__stu_list[j + 1] = temp_stu
class StudentManagerView:
'''
学生管理界面视图
'''
def __init__(self):
self.__manager = StudentManagerController()
def __display_menu(self):
'''
显示菜单
'''
print("1)添加学生")
print("2)显示学生")
print("3)删除学生")
print("4)修改学生")
print("5)按照成绩升序显示学生")
def __select_menu(self):
'''
选择菜单
'''
item = input("请输入:n")
if item == "1":
self.__input_student()
elif item == "2":
self.__output_students()
elif item == "3":
number_ = int(input("请输入学生编号:n"))
self.__delete_student(number_)
elif item == "4":
self.__modify_student()
elif item == "5":
self.__output_student_by_score()
def __input_student(self):
'''
输入学生
'''
name = input("请输入学生姓名:n")
age = int(input("请输入学生年龄:n"))
score = int(input("请输入学生成绩:n"))
stu = StudentModel(name, age, score)
self.__manager.add_student(stu)
def __output_students(self):
'''
输出学生
'''
for item in self.__manager.stu_list:
print(item.name, item.age, item.score, item.id)
def __delete_student(self, number):
'''
删除学生
:param number: 学生编号与number相等,删除学生
'''
if self.__manager.remove_student(number):
print("删除成功")
else:
print("删除失败")
def __modify_student(self):
'''
修改学生
'''
stu_num = int(input("请输入要修改学生的编号:n"))
if self.__manager.update_student(stu_num):
print("修改成功")
else:
print("修改失败")
def __output_student_by_score(self):
'''
根据成绩输出学生
'''
self.__manager.order_by_score()
self.__output_students()
def main(self):
'''
主函数,程序入口
'''
while True:
self.__display_menu()
self.__select_menu()
view = StudentManagerView()
view.main()
2. 继承
2.1 说明
- 多个子类在概念上一致的,所以就抽象出一个父类
- 多个子类的共性,可以提取到父类中
- isinstance(对象,类型):判断对象是不是属于一个类型,返回一个bool值
- issubclass(类型,类型):判断一个类型是否属于另一个类型,返回一个bool值
子类若没有构造函数,使用父类的
class Person:
def __init__(self, name):
self.name = name
class Student(Person):
pass
s01 = Student("张三")
子类若有构造函数,则必须先调用父类构造函数
class Person:
def __init__(self, name):
self.name = name
class Student(Person):
def __init__(self, score, name):
super().__init__(name)
self.score = score
class Person:
def __init__(self, name):
self.name = name
class Student(Person):
def __init__(self, score, name):
Person.__init__(self, name)
self.score = score
3. 多态
3.1 定义
- 父类的同一种动作或行为,在不同的子类上有不同的实现
- 子类实现了父类中相同的方法(方法名、参数),在调用该方法时,实际调用的是子类的方法
- python中,以双下划线开头、双下划线结尾的是系统定义的成员。我们可以在自定义类中进行重写,从而改变其行为。
在 Python 中要将某一类型的变量或者常量转换为字符串对象通常有两种方法,即 str() 或者 repr() 。
- str() 用于将值转化为适于人阅读的形式
- repr() 转化为供解释器读取的形式(如果没有等价的语法,则会发生SyntaxError 异常), 适合开发和调试阶段使用。
- 作用:用于将字符串转化成Python代码执行
- 作用:将对象转换成字符串(对人友好的:随意格式)
- 代码示例
class Cat:
"""定义一个猫类"""
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
"""返回一个对象的描述信息"""
return "名字是:%s , 年龄是:%d" % (self.name, self.age)
tom = Cat("汤姆", 30)
print(tom)
print(str(tom))
# 运行结果:
名字是:汤姆 , 年龄是:30
名字是:汤姆 , 年龄是:30
3.3.4 __ repr __( self )
- 将对象转换成字符串(供解释器读取的形式)
class Cat:
"""定义一个猫类"""
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return "Cat('%s', %d)" % (self.name, self.age)
tom = Cat("汤姆", 30)
print(repr(tom))
# 运行结果:
Cat('汤姆', 30)
3.4 运算符重载
3.4.1 定义
- 让自定义的类生产的对象(实例)能够使用运算符进行操作
class Num:
def __init__(self, x):
self.x = x
def __str__(self):
return "打印的对象的x值是:" + str(self.x)
def __add__(self, other):
return Num(self.x + other)
num = Num(10)
print(num + 2)
# 运行结果:
打印的对象的x值是:12
3.4.3 反向算术运算符重载
3.4.4 复合运算符重载
重写__iadd__(self, rhs)实现在原对象基础上的变化,如果不重写__iadd__(self, rhs)默认使用__add__(self, rhs),一般会产生新对象
class Vehicle:
'''
交通工具,代表所有具体的交通工具(火车/飞机)
继承:隔离子类变化,将子类的共性提取到父类中
'''
def transport(self, position):
# 若子类没有重写父类的方法,客户端代码调用go_to方法时会执行父类的方法
raise NotImplementedError()
class Person:
def __init__(self, name):
self.name = name
def go_to(self, vehicle, position):
# 多态:调用父,执行子
# 调用的是交通工具的运输方法,执行的是飞机的运输方法或者汽车的运输方法
if isinstance(vehicle, Vehicle):
vehicle.transport(position)
# ----------以上是架构师完成的----------以下是程序员完成的----------
class Car(Vehicle):
def transport(self, position):
print("汽车开到", position)
class AirPlane(Vehicle):
def transport(self, position):
print("飞机飞到", position)
p1 = Person("老张")
c1 = Car()
a1 = AirPlane()
p1.go_to(c1, "东北")
p1.go_to(a1, "东北")
4. 设计原则
4.1 开闭原则(目标,总的指导思想)
- 对扩展开放,对修改关闭
- 增加新功能,不改变原有代码
- 一个类有且只有一个改变它的原因
- 客户端代码(调用的类)尽量依赖(使用)抽象的组件
- 如果仅仅为了代码复用优先选择组合复用,而非继承复用
- 组合的耦合性相对继承低
- 父类出现的地方可以被子类替换,替换后依然保持原功能
- 子类要拥有父类的所有功能
- 子类在重写父类方法时,尽量选择扩展重写,防止改变了功能
- 不要和陌生人说话
- 类与类交互时,在满足功能要求的基础上,传递的数据量越少越好。因为这样可能降低耦合度
- 子类与父类的关系,概念的复用,耦合度最高
- B类泛化A类,意味B类是A类的一种;
- 做法:B类继承A类
- 部分与整体的关系,功能的复用,变化影响一个类
- A与B关联,意味着B是 A的一部分
- 做法:在A类中包括B类型成员
- 合作关系,一种相对松散的协作,变化影响一个方法;
- A类依赖B类,意味A类的某些功能靠B类实现;
- 做法:B类型作为A类中方法的参数,并不是A的成员。
'''技能系统'''
class SkillImpactEffect:
'''技能影响效果'''
def impact(self):
raise NotImplementedError()
class DamageEffect(SkillImpactEffect):
'''伤害生命效果'''
def __init__(self, value):
self.value = value
def impact(self):
print("扣你%d血" % self.value)
class LowerDefenseEffect(SkillImpactEffect):
'''降低防御力'''
def __init__(self, value, time):
self.value = value
self.time = time
def impact(self):
print("降低%d防御力,持续%d秒" % (self.value, self.time))
class DizzinessEffect(SkillImpactEffect):
'''眩晕'''
def __init__(self, time):
self.time = time
def impact(self):
print("眩晕%d秒" % self.time)
class SkillDeployer:
'''技能释放器'''
def __init__(self, name):
self.name = name
# 获取配置文件 格式 -> {技能名称:[效果1,效果2,...],...}
self.__dict_skill_config = SkillDeployer.__load_config_file()
# 创建效果对象
self.__effect_objects = self.__create_effect_object()
@staticmethod
def __load_config_file():
'''
加载配置文件
:return: 字典格式的配置文件
'''
# 加载配置文件
return {
"降龙十八掌": ["DamageEffect(200)", "LowerDefenseEffect(-10, 5)", "DizzinessEffect(6)"],
"六脉神剑": ["DamageEffect(100)", "DizzinessEffect(6)"],
}
def __create_effect_object(self):
'''
创建技能对象
:return: 技能对象列表
'''
list_effect_name = self.__dict_skill_config[self.name] # 根据name创建相应的技能对象
list_effect_object = []
for item in list_effect_name:
list_effect_object.append(eval(item))
return list_effect_object
def generate_skill(self):
'''生成技能(执行效果)'''
print("%s技能释放" % self.name)
for item in self.__effect_objects:
# 调用父类,执行子类
item.impact()
xl18z = SkillDeployer("降龙十八掌")
xl18z.generate_skill()
P3 模块和包
1. 模块的导入
1.1 import
- 语法
import 模块名
import 模块名 as 别名 - 作用
将某模块整体导入到当前模块中 - 使用
模块名.成员
- 语法
from 模块名 import 成员名 [as 别名] - 作用
将其他模块的成员导入到当前模块的作用域
- 语法
from 模块名 import * - 作用
将某模块的所有成员导入到当前模块 - 说明
模块中以单下划线(_)开头的属性不会被导入,通常称这些成员为隐藏成员
进入manager_system目录位置
进入__pycache__,有三个后缀是.pyc结尾的文件
Python是解释型语言,但为了提高运行速度,使用了一种编译的方法。编译之后得到pyc文件,存储了字节码(特定于Python的表现形式,不是机器码)
编译器将源代码转化成pyc格式的物理文件,以后运行时,解释器直接解释这些pyc格式的物理文件
编译器没有编译main.py文件,因此不要把大段代码放在最开始运行的模块中
定义当前模块哪些成员可以被from 模块名 import *形式导入
__all__ = ["fun01", "_fun02", "MyClass"]
def fun01():
pass
def _fun02():
pass
class MyClass:
pass
3.2 __doc__变量
文档字符串,可以通过该属性查看文档注释
'''模块1''' print(__doc__) # 运行结果是 模块13.3 __file__变量
模块对应的文件名的绝对路径(绝对路径:从系统根目录开始计算的)
3.4 __name__变量模块自身名字,可以判断是否为主模块。当此模块作为主模块(第一个运行的模块)运行时,__name__绑定’main’,不是主模块,而是被其他模块导入时,存储模块名。
if __name__ == '__main__':
# 如果从当前模块运行,这里面的代码会执行
# 如果不是从该模块运行,这里面的代码不会执行
pass
4. time模块
'''时间处理'''
import time
# 获取当前时间戳
print(time.time())
# 时间元组
print(time.localtime()) # 不传参,打印当前时间戳对应的时间元组
time_stamp = 1636276781
print(time.localtime(time_stamp)) # 传参,打印传递的时间戳对应的时间元组
# 时间元组 --> 时间戳
print(time.mktime(time.localtime()))
# 时间元组 --> str
str_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
print(str_time)
# str --> 时间元组
print(time.strptime(str_time, "%Y-%m-%d %H:%M:%S"))
5. 包
5.1 导入
- import 包名 [as 包别名] 需要设置__all__
- import 包名.模块名 [as 模块新名]
- import 包名.子包名.模块名 [as 模块新名]
- from 包名 import 模块名 [as 模块新名]
- from 包名.子包名 import 模块名 [as 模块新名]
- from 包名.子包名.模块名 import 成员名 [as 属性新名]
导入包内的所有子包和模块
- from 包名 import *
- from 包名.模块名 import *
- 是包内必须存在的文件
- 会在包加载时被自动调用
记录from 包 import *语句需要导入的模块
要把A包的所有模块导过来:
- from A import *
- 把__all__放在A包的__init__.py文件中
__ all __ = [“模块1”, “模块2”, …]
import sys
print(sys.path) # 导包成功与否看sys.path里面有无要导的包所在的路径
sys.path.append('要导的包所在的路径')
P4 异常
1. 定义
- 运行时检测到错误
- 当异常发生时,程序不会再向下执行,而转到函数的调用语句
- 名称异常(NameError):变量未定义
- 类型异常(TypeError):不同类型数据进行运算
- 索引异常(IndexError):超出索引范围
- 属性异常(AttributeError):没有对应名称的属性
- 键异常(KeyError):没有对应名称的键
- 未实现异常(NotImplementError):尚未实现的方法
- 异常基类Exception
try:
可能发生异常的语句
except 错误类型1 [as 变量1]:
处理语句1
except 错误类型2 [as 变量2]:
处理语句2
except Exception [as 变量3]:
不是以上错误类型的语句
else:
未发生异常的语句
finally:
无论是否发生异常都执行的语句
4.2 作用
将程序由异常状态转为正常流程
4.3 说明- as子句是用于绑定错误对象的变量,可以省略
- except子句可以有一个或多个
- else子句最多只能有一个
def div_apple(count):
number = int(input("请输入人数"))
return "每人%d个苹果" % (count / number)
try:
div_apple(10)
except ValueError:
print("输入的人数必须是整数")
except ZeroDivisionError:
print("输入的人数不可为0")
except Exception:
print("未知错误")
else:
# 如果异常不执行else语句
print("没有出现异常才执行的代码")
finally:
# 有finally不写except子句语法上不错误
print("无论是否出现异常一定会执行的代码")
print("后续逻辑...")
5. raise语句
5.1 作用
- 抛出一个错误,让程序进入异常状态
- 在程序调用层数较深时,向主调函数传递错误信息要层层return比较麻烦,而异常是一种天然的向上返回的机制。所以人为抛出异常,可以直接传递错误信息
class XxxError(Exception):
def __init__(self,参数):
super().__init__(参数)
self.数据 = 参数
5.3.2 作用
- 当发生错误时,想向上返回多个错误信息时,可以将这些错误信息用自定义异常类封装
class AgeError(Exception):
'''
自定义异常类,用来封装错误信息
'''
def __init__(self, msg, age_value, code_line, error_number):
# super().__init__("调用父类的构造函数")
self.msg = msg
self.age_value = age_value
self.code_line = code_line
self.error_number = error_number
class Student:
def __init__(self, age):
self.age = age
@property
def age(self):
return self.__age
@age.setter
def age(self, value):
if 21<=value<=31:
self.__age = value
else:
raise AgeError("年龄范围错误",value,25,1001)
try:
s1 = Student(36)
except AgeError as e:
print("出错了")
print(e.msg)
print(e.age_value)
print(e.code_line)
P5 迭代
1. 迭代器
1.1 迭代定义
- 每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。例如:循环获取容器中的元素。
- 可以调用__next__()方法并返回下一个值的对象
- 具有__iter__()方法的对象
- 可以返回迭代器的对象
--创建:
class 可迭代对象名称:
def __iter__(self):
return 迭代器
--使用:
for 变量名 in 可迭代对象:
语句
1.2.3 原理
- 获取迭代器
- 循环获取下一个元素
- 遇到异常停止迭代
迭代器对象 = 可迭代对象.__iter__() while True: try: print(迭代器对象.__next__()) except StopIteration: break1.2.4 代码示例
list_ = ['physics', 'chemistry', 1997, 2000]
# 获取迭代器
iterator = list_.__iter__()
# 循环获取下一个元素
while True:
try:
item = iterator.__next__()
print(item)
except StopIteration: # 遇到异常停止迭代
break
1.3 迭代器的代码实现
'''迭代器'''
class Employee:
pass
class EmployeeIteration:
def __init__(self, iterative_object):
self.__iterative_object = iterative_object
self.__index = 0
def __next__(self):
if self.__index > len(self.__iterative_object) - 1:
raise StopIteration
temp = self.__iterative_object[self.__index]
self.__index += 1
return temp
class EmployeeManager:
def __init__(self):
self.__employees_list = []
def add_employee(self, employee):
self.__employees_list.append(employee)
def __iter__(self):
return EmployeeIteration(self.__employees_list)
manager = EmployeeManager()
manager.add_employee(Employee)
manager.add_employee(Employee)
manager.add_employee(Employee)
# 如何把一个自定义的类创建的对象用for进行遍历?
# for循环的原理是什么?能被for的条件是什么?
for item in manager:
print(item)
employees_iteration = manager.__iter__()
while True:
try:
print(employees_iteration.__next__())
except StopIteration:
break
1.4 yield
'''
创建一个自定义类的对象MyRange(int值),
实现能够输出0--(int值) - 1的功能
'''
class Num:
pass
class NumIterator:
def __init__(self, value):
self.value = value
self.__number = 0
def __next__(self):
if self.__number > self.value - 1:
raise StopIteration
temp = self.__number
self.__number += 1
return temp
class MyRange:
def __init__(self, num):
self.__num = num
def __iter__(self):
return NumIterator(self.__num)
my_range = MyRange(10)
for item in my_range:
print(item)
上面代码与下面代码做的是同一件事,下面代码做的是迭代器 --> yield的转变
'''
创建一个自定义类的对象MyRange(int值),
实现能够输出0--(int值) - 1的功能
'''
class Num:
pass
class MyRange:
def __init__(self, num):
self.__num = num
def __iter__(self):
# yield作用:将下列代码改为迭代器模式的代码
# 生成迭代器代码的大致规则
# 1.将yield前面的代码定义在__next__()方法中
# 2.将yield后面的数据作为__next__()方法返回值
number = 0
while number < self.__num:
yield number
number += 1
my_range = MyRange(10)
for item in my_range:
print(item)
我们再把上面代码进行改写,实现yield到生成器的转变
def my_range(num):
number = 0
while number < num:
yield number
number += 1
for item in my_range(10):
print(item)
2. 生成器generator
2.1 定义
- 能够动态(循环一次计算一次返回一次)提供数据的可迭代对象
- 在循环过程中,按照某种算法推算数据,不必创建容器储存储完整的结果,从而节省内存空间。数据量越大,优势越明显。
以上作用也称之为延迟操作或惰性操作,通俗的讲就是在需要的时候才计算结果,而不是一次构建出所有结果
- 含有yield语句的函数,返回值为生成器对象
# 创建生成器函数
def fun_generate():
...
yield data
...
# 调用生成器函数
for variable_name in fun_generate():
the statement to execute
2.3.3 说明
- 调用生成器函数将返回一个生成器对象
- yield翻译为“产生”或“生成”
- 调用生成器函数会自动创建迭代器对象
- 调用迭代器对象的__next__()方法时才执行生成器函数
- 每次执行到yield语句时返回数据,暂时离开
- 待下次调用__next__()方法时继续从离开处继续执行
生成迭代器对象的大致规则如下
- 将yield关键字以前的代码放在__next__()方法中
- 将yield关键字后面的数据作为__next__()方法的返回值
''' 定义生成器函数my_enumerate,实现将元素与索引合成一个元组 '''
list_ = ["a", "b", "c", "d", "e"]
# for item in enumerate(list_):
# print(item)
def my_enumerate(list_target):
for i in range(len(list_target)):
yield i, list_target[i]
for i1, i2 in my_enumerate(list_):
print(i1, i2)
''' 定义生成器函数my_zip,实现将多个列表的每个元素合成一个元组 '''
list01 = ["A", "B", "C", "D"]
list02 = [0, 1, 2, 3]
# for item in zip(list01, list02):
# print(item)
def my_zip(iterable1, iterable2):
for i in range(len(min(iterable1, iterable2))):
yield (iterable1[i], iterable2[i])
# for item in my_zip(list01, list02):
# print(item)
2.4 生成器类的源码实现
class MyGenerator:
'''生成器原理代码实现 生成器 = 可迭代对象 + 迭代器'''
def __init__(self, stop_value):
self.stop_value = stop_value
self.begin = 0
def __iter__(self):
'''
:return: 返回的是自己。为什么生成器对象调用__iter__()方法返回的是自己?
因为print(id(生成器.__iter__()))和print(id(生成器))是相同的
'''
return self
def __next__(self):
'''
为什么生成器对象具有__next__()方法?
print(dir(生成器对象))可以看到生成器对象的所有实例成员,
其中就有__next__()方法
'''
if self.begin >= self.stop_value:
raise StopIteration
temp = self.begin
self.begin += 1
return temp
P7 函数式编程
1. 定义
用一系列函数解决问题
- 函数可以赋值给变量,赋值后变量绑定函数
- 允许将函数作为参数传入另一个函数
- 允许函数返回一个函数
高阶函数
- 将函数作为参数或返回值的函数
将核心逻辑传入方法体,使该方法的适用性更广,体现了面向对象的开闭原则
2.1 函数式编程思想的代码体现class SkillData:
def __init__(self, id, name, atk_ratio, duration):
self.id = id
self.name = name
self.atk_ratio = atk_ratio
self.duration = duration
def __str__(self):
return "技能数据是:%d,%s,%d,%d" % (self.id, self.name, self.atk_ratio, self.duration)
list_skill = [
SkillData(101, "乾坤大挪移", 5, 10),
SkillData(102, "降龙十八掌", 8, 5),
SkillData(103, "葵花宝典", 10, 3),
]
# 需求1:获取攻击比例大于6的所有技能
def fun01():
for item in list_skill:
if item.atk_ratio > 6:
yield item
# 需求2:获取持续时间在4-11之间的所有技能
def fun02():
for item in list_skill:
if 4 < item.duration < 11:
yield item
# 需求3:获取技能名称大于4个字并且持续时间小于6的所有技能
def fun03():
for item in list_skill:
if len(item.name) > 4 and item.duration < 6:
yield item
'''以上每个需求的代码大量重复,我们知道代码的重复是万恶之源,可以用函数式编程的思想对代码进行重构'''
# “封装”(分而治之 变则疏之)
# 将每个变化的条件,单独定义在函数中
def condition01(item):
return item.atk_ratio > 6
def condition02(item):
return 4 < item.duration < 11
def condition03(item):
return len(item.name) > 4 and item.duration < 6
# “继承”(隔离变化)
def find(func_condition):
'''
通用的查找方法
:param func_condition:查找条件,函数类型
函数名(变量) --> 返回值bool类型
:return:需要查找的元素,生成器类型
'''
for item in list_skill:
if func_condition(item):
yield item
# for item in find(condition01):
# print(item)
2.2 lambda
2.2.1 定义
- 是一种匿名方法
- 是天然的实参
- 作为函数传递时语法简洁,优雅,代码可读性强
- 随时创建和销毁,减少程序耦合度
- 定义:变量 = lambda 形参:方法体
- 调用:变量(实参)
- 形参没有可以不填
- 方法体只能有一条语句,且不支持赋值语句
- map(函数, 可迭代对象):使用可迭代对象中的每个元素调用函数,将返回值作为新可迭代对象元素;返回值为新可迭代对象
- filter(函数, 可迭代对象):根据条件筛选可迭代对象中的元素,返回值为新可迭代对象
- sorted(可迭代对象, key = 函数, reverse = bool值):排序,返回值为排序结果
- max(可迭代对象, key = 函数):根据函数获取可迭代对象的最大值
- min(可迭代对象, key = 函数):根据函数获取可迭代对象的最小值
class Enemy:
def __init__(self, name, atk, defense, hp):
self.name = name
self.atk = atk
self.defense = defense
self.hp = hp
def __str__(self):
return "%s" % self.name
list_enemy = [
Enemy("玄冥", 3, 12, 20),
Enemy("灭霸", 7, 5, 10),
Enemy("成昆", 5, 1, 0),
Enemy("公孙", 2, 10, 30),
]
# 获取所有血量为0的敌人 filter
for item in filter(lambda item: item.hp == 0, list_enemy):
print(item)
# 获取所有敌人姓名 map
for item in map(lambda item: item.name, list_enemy):
print(item)
print("----")
# 获取攻击力值最大的敌人 max
print(max(list_enemy, key=lambda item: item.atk))
# 将敌人列表根据防御力升序排列 sorted
result = sorted(list_enemy, key=lambda item: item.defense)
for item in result:
print(item)
# 将敌人列表根据防御力降序排列 sorted
result = sorted(list_enemy, key=lambda item: item.defense, reverse=True)
for item in result:
print(item)
3. 函数作为返回值
3.1 外部嵌套作用域
代码示例:
'''外部嵌套作用域'''
def fun01():
# a是fun01函数的局部作用域
# a也是fun02函数的外部嵌套作用域
a = 1
def fun02():
# fun02可以访问外部嵌套作用域变量
# a = 2 # 创建了fun02的局部变量
nonlocal a # 声明外部嵌套作用域
a = 2 # 修改外部嵌套作用域a的值
fun02()
print(a) # 2
fun01()
3.2 闭包
3.2.1 三要素
- 必须有一个内嵌函数
- 内嵌函数必须引用外部函数中的变量
- 外部函数返回值必须是内嵌函数
- 定义:
def external_function(parameter):
external_variable
def inner_function(arguments):
use external_variable
return inner_function
- 调用:
variable = external_function(parameter) variable(arguments)
代码示例
'''闭包'''
def fun01():
a = 1
def fun02():
print(a)
return fun02
# 调用外部函数,返回值是内嵌函数
result = fun01()
# 调用内嵌函数
result()
3.3.3 说明
定义:
- 在一个函数内部的函数,同时内部函数又引用了外部函数的变量
本质:
- 闭包是将内部函数和外部函数的执行环境绑定在一起的对象
优点:
- 内部函数可以使用外部变量
缺点:
- 外部变量一直存在于内存中,不会在调用结束后释放,占用内存
函数作为返回值:
- 逻辑连续,当内部函数被调用时,不脱离当前的逻辑
先看需求:对进入后台和删除订单两个功能增加权限验证
def verify_permissions():
print("权限验证")
def enter_background():
verify_permissions()
print("进入后台")
def delete_order():
verify_permissions()
print("删除订单")
enter_background()
delete_order()
上面代码违反了设计原则中的开闭原则
# 用函数式编程-闭包的思想改写上面代码
def verify_permissions(func):
def wrapper():
print("权限验证")
func()
return wrapper
def enter_background():
print("进入后台")
def delete_order():
print("删除订单")
enter_background = verify_permissions(enter_background)
delete_order = verify_permissions(delete_order)
enter_background()
delete_order()
上面代码存在着每次拦截对已有功能的调用,显得不合理
# 用装饰器改写上面的代码
def verify_permissions(func):
def wrapper():
print("权限验证")
func()
return wrapper
@verify_permissions
def enter_background():
print("进入后台")
@verify_permissions
def delete_order():
print("删除订单")
enter_background()
delete_order()
上面代码,如果已有功能参数不统一,则无法包装
# 解决了已有功能的参数不统一而无法包装的问题
def verify_permissions(func):
def wrapper(*args, **kwargs):
print("权限验证")
func(*args, **kwargs)
return wrapper
@verify_permissions
def enter_background(login_id, pwd):
print("用户%s,密码%s,进入后台" % (login_id, pwd))
@verify_permissions
def delete_order(order_id):
print("删除订单", order_id)
enter_background("abc", 123)
delete_order(101)
3.3.2 函数装饰器decorators
- 定义:在不改变原函数的调用以及内部代码情况下,为其增加新功能的函数
- 语法:
def decorator_name(func):
def inline_function(*args, **kwargs):
需要添加的新功能
return func(*args, **kwargs)
return inline_function
@decorator_name
def original_function_name(arguments):
function
body
original_function_name(arguments)



