'''实参''' 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): pass2. 可变与不可变类型在传参时的区别 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] = 100P2 面向对象 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 = value1.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 = score3. 多态 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 名字是:汤姆 , 年龄是:303.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值是:123.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: pass3.2 __doc__变量
文档字符串,可以通过该属性查看文档注释
'''模块1''' print(__doc__) # 运行结果是 模块13.3 __file__变量
模块对应的文件名的绝对路径(绝对路径:从系统根目录开始计算的)
3.4 __name__变量模块自身名字,可以判断是否为主模块。当此模块作为主模块(第一个运行的模块)运行时,__name__绑定’main’,不是主模块,而是被其他模块导入时,存储模块名。
if __name__ == '__main__': # 如果从当前模块运行,这里面的代码会执行 # 如果不是从该模块运行,这里面的代码不会执行 pass4. 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: # 遇到异常停止迭代 break1.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: break1.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 execute2.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 tempP7 函数式编程 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)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)