Python语言核心编程

Python语言核心编程,第1张

Python语言核心编程 P1 面向过程 1. 实参与形参
'''实参'''

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,字符串,元组,固定集合…
2.2 常见可变类型参数
  • 列表,字典,可变集合…
2.3 传参说明
  • 不可变类型数据传参时,函数内部不会改变原数据的值
  • 可变类型数据传参时,函数内部改变原数据
2.4 代码演示
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. 封装
  1. 数据角度讲: 将一些基本数据类型复合成一个自定义类型
  2. 行为角度讲:向类外提供必要的功能,隐藏实现的细节
  3. 设计角度讲
    – 分而治之:将一个大的需求分解为许多类,每个类处理一个独立的功能
    – 变则疏之:变化的地方独立封装,避免影响其他类
    – 高内聚:类中各个方法都在完成一项任务(单一职责的类)
    – 低耦合:类与类的关联性上与依赖度要低(每个类独立)
1.1 属性@property对象 1.1.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)

说明:

  • 公开的实例变量,缺少逻辑验证。私有的实例变量与两个公开的方法相结合,又使调用者的 *** 作略显复杂,而属性可以将两个方法的使用方式像 *** 作变量一样方便。
1.2 __ slots __ 1.2.1 作用
  • 限定一个类创建的对象只能有固定的实例变量
1.2.2 示例
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 备注
  1. 含有__slots__属性的类所创建的对象没有__dict__属性
    备注: __ dict __:是python内置的属性,用来存储对象的实例变量
  2. 优点:防止用户因错写属性的名称而发生程序错误
  3. 缺点:丧失了动态语言可以在运行时为对象添加变量的灵活性
1.3 基于MVC的学生管理系统
'''学生管理系统'''


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 说明
  • 多个子类在概念上一致的,所以就抽象出一个父类
  • 多个子类的共性,可以提取到父类中
2.2 isinstance 与 issubclass
  • isinstance(对象,类型):判断对象是不是属于一个类型,返回一个bool值
  • issubclass(类型,类型):判断一个类型是否属于另一个类型,返回一个bool值
2.3 子类的构造函数

子类若没有构造函数,使用父类的

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 定义
  • 父类的同一种动作或行为,在不同的子类上有不同的实现
3.2 重写
  • 子类实现了父类中相同的方法(方法名、参数),在调用该方法时,实际调用的是子类的方法
3.3 内置可重写函数
  • python中,以双下划线开头、双下划线结尾的是系统定义的成员。我们可以在自定义类中进行重写,从而改变其行为。
3.3.1 str()与repr()

       在 Python 中要将某一类型的变量或者常量转换为字符串对象通常有两种方法,即 str() 或者 repr() 。

  • str() 用于将值转化为适于人阅读的形式
  • repr() 转化为供解释器读取的形式(如果没有等价的语法,则会发生SyntaxError 异常), 适合开发和调试阶段使用。
3.3.2 eval()与exec()
  • 作用:用于将字符串转化成Python代码执行
3.3.3 __ str__(self )
  • 作用:将对象转换成字符串(对人友好的:随意格式)
  • 代码示例
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 定义
  • 让自定义的类生产的对象(实例)能够使用运算符进行 *** 作
3.4.2 算术运算符重载

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),一般会产生新对象

3.5 封装的代码示例
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 开闭原则(目标,总的指导思想)
  • 对扩展开放,对修改关闭
  • 增加新功能,不改变原有代码
4.2 类的单一职责(一个类的定义)
  • 一个类有且只有一个改变它的原因
4.3 依赖倒置(依赖抽象)
  • 客户端代码(调用的类)尽量依赖(使用)抽象的组件
4.4 组合复用原则(复用的最佳实践)
  • 如果仅仅为了代码复用优先选择组合复用,而非继承复用
  • 组合的耦合性相对继承低
4.5 里氏替换(继承后的重写,指导继承的设计)
  • 父类出现的地方可以被子类替换,替换后依然保持原功能
  • 子类要拥有父类的所有功能
  • 子类在重写父类方法时,尽量选择扩展重写,防止改变了功能
4.6 迪米特法则(类与类交互的原则)
  • 不要和陌生人说话
  • 类与类交互时,在满足功能要求的基础上,传递的数据量越少越好。因为这样可能降低耦合度
5. 类与类之间的关系 5.1 泛化:
  • 子类与父类的关系,概念的复用,耦合度最高
  • B类泛化A类,意味B类是A类的一种;
  • 做法:B类继承A类
5.2 关联(聚合/组合):
  • 部分与整体的关系,功能的复用,变化影响一个类
  • A与B关联,意味着B是 A的一部分
  • 做法:在A类中包括B类型成员
5.3 依赖:
  • 合作关系,一种相对松散的协作,变化影响一个方法;
  • A类依赖B类,意味A类的某些功能靠B类实现;
  • 做法:B类型作为A类中方法的参数,并不是A的成员。
6. 面向对象综合
'''技能系统'''


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
  1. 语法
        import 模块名
        import 模块名 as 别名
  2. 作用
        将某模块整体导入到当前模块中
  3. 使用
        模块名.成员
1.2 from import
  1. 语法
        from 模块名 import 成员名 [as 别名]
  2. 作用
        将其他模块的成员导入到当前模块的作用域
1.3 from import *
  1. 语法
        from 模块名 import *
  2. 作用
        将某模块的所有成员导入到当前模块
  3. 说明
        模块中以单下划线(_)开头的属性不会被导入,通常称这些成员为隐藏成员
2. __ pycache__


进入manager_system目录位置


进入__pycache__,有三个后缀是.pyc结尾的文件

Python是解释型语言,但为了提高运行速度,使用了一种编译的方法。编译之后得到pyc文件,存储了字节码(特定于Python的表现形式,不是机器码)

编译器将源代码转化成pyc格式的物理文件,以后运行时,解释器直接解释这些pyc格式的物理文件


编译器没有编译main.py文件,因此不要把大段代码放在最开始运行的模块中

3. 模块变量 3.1 __all__变量

定义当前模块哪些成员可以被from 模块名 import *形式导入

__all__ = ["fun01", "_fun02", "MyClass"]

def fun01():
    pass

def _fun02():
    pass

class MyClass:
    pass
3.2 __doc__变量

文档字符串,可以通过该属性查看文档注释

'''模块1'''

print(__doc__)

# 运行结果是
模块1
3.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   *
5.2 __ init__.py文件
  • 是包内必须存在的文件
  • 会在包加载时被自动调用
5.3 __ all__

记录from   包   import   *语句需要导入的模块
要把A包的所有模块导过来:

  1. from   A   import   *
  2. 把__all__放在A包的__init__.py文件中
    __ all __ = [“模块1”,  “模块2”,  …]
5.4 搜索顺序
import sys

print(sys.path) # 导包成功与否看sys.path里面有无要导的包所在的路径
sys.path.append('要导的包所在的路径')
P4 异常 1. 定义
  • 运行时检测到错误
2. 现象
  • 当异常发生时,程序不会再向下执行,而转到函数的调用语句
3. 常见异常类型
  • 名称异常(NameError):变量未定义
  • 类型异常(TypeError):不同类型数据进行运算
  • 索引异常(IndexError):超出索引范围
  • 属性异常(AttributeError):没有对应名称的属性
  • 键异常(KeyError):没有对应名称的键
  • 未实现异常(NotImplementError):尚未实现的方法
  • 异常基类Exception
4.异常处理 4.1 语法
    try:
        可能发生异常的语句
    except 错误类型1 [as 变量1]:
        处理语句1
    except 错误类型2 [as 变量2]:
        处理语句2
    except Exception [as 变量3]:
        不是以上错误类型的语句
    else:
        未发生异常的语句
    finally:
        无论是否发生异常都执行的语句
4.2 作用

将程序由异常状态转为正常流程

4.3 说明
  • as子句是用于绑定错误对象的变量,可以省略
  • except子句可以有一个或多个
  • else子句最多只能有一个
4.4 代码演示
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 作用
  • 抛出一个错误,让程序进入异常状态
5.2 目的
  • 在程序调用层数较深时,向主调函数传递错误信息要层层return比较麻烦,而异常是一种天然的向上返回的机制。所以人为抛出异常,可以直接传递错误信息
5.3 自定义异常类 5.3.1 定义
class XxxError(Exception):
    def __init__(self,参数):
        super().__init__(参数)
        self.数据 = 参数
5.3.2 作用
  • 当发生错误时,想向上返回多个错误信息时,可以将这些错误信息用自定义异常类封装
5.3.3 代码演示
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 迭代定义
  • 每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。例如:循环获取容器中的元素。
1.2 可迭代对象iterable 1.2.1 定义
  • 可以调用__next__()方法并返回下一个值的对象
  • 具有__iter__()方法的对象
  • 可以返回迭代器的对象
1.2.2 语法
--创建:
    class 可迭代对象名称:
        def __iter__(self):
            return 迭代器
--使用:
    for 变量名 in 可迭代对象:
        语句
1.2.3 原理
  1. 获取迭代器
  2. 循环获取下一个元素
  3. 遇到异常停止迭代
迭代器对象 = 可迭代对象.__iter__()
while True:
	try:
		print(迭代器对象.__next__())
	except StopIteration:
		break
1.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 定义
  • 能够动态(循环一次计算一次返回一次)提供数据的可迭代对象
2.2 作用
  • 在循环过程中,按照某种算法推算数据,不必创建容器储存储完整的结果,从而节省内存空间。数据量越大,优势越明显。
    以上作用也称之为延迟 *** 作或惰性 *** 作,通俗的讲就是在需要的时候才计算结果,而不是一次构建出所有结果
2.3 生成器函数 2.3.1 定义
  • 含有yield语句的函数,返回值为生成器对象
2.3.2 语法
# 创建生成器函数
    def fun_generate():
        ...
        yield data
        ...
        
# 调用生成器函数
    for variable_name in fun_generate():
        the statement to execute
        
2.3.3 说明
  • 调用生成器函数将返回一个生成器对象
  • yield翻译为“产生”或“生成”
2.3.4 执行过程
  • 调用生成器函数会自动创建迭代器对象
  • 调用迭代器对象的__next__()方法时才执行生成器函数
  • 每次执行到yield语句时返回数据,暂时离开
  • 待下次调用__next__()方法时继续从离开处继续执行
2.3.5 原理

生成迭代器对象的大致规则如下

  • 将yield关键字以前的代码放在__next__()方法中
  • 将yield关键字后面的数据作为__next__()方法的返回值
2.3.6 生成器函数enumerate和zip
''' 定义生成器函数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. 函数作为参数

将核心逻辑传入方法体,使该方法的适用性更广,体现了面向对象的开闭原则

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 定义
  • 是一种匿名方法
  • 是天然的实参
2.2.2 作用
  • 作为函数传递时语法简洁,优雅,代码可读性强
  • 随时创建和销毁,减少程序耦合度
2.2.3 语法
  • 定义:变量 = lambda 形参:方法体
  • 调用:变量(实参)
2.2.4 说明
  • 形参没有可以不填
  • 方法体只能有一条语句,且不支持赋值语句
2.3 内置高阶函数
  1. map(函数, 可迭代对象):使用可迭代对象中的每个元素调用函数,将返回值作为新可迭代对象元素;返回值为新可迭代对象
  2. filter(函数, 可迭代对象):根据条件筛选可迭代对象中的元素,返回值为新可迭代对象
  3. sorted(可迭代对象, key = 函数, reverse = bool值):排序,返回值为排序结果
  4. max(可迭代对象, key = 函数):根据函数获取可迭代对象的最大值
  5. 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 三要素
  • 必须有一个内嵌函数
  • 内嵌函数必须引用外部函数中的变量
  • 外部函数返回值必须是内嵌函数
3.2.2 语法:
  • 定义:
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 说明

定义:

  • 在一个函数内部的函数,同时内部函数又引用了外部函数的变量

本质:

  • 闭包是将内部函数和外部函数的执行环境绑定在一起的对象

优点:

  • 内部函数可以使用外部变量

缺点:

  • 外部变量一直存在于内存中,不会在调用结束后释放,占用内存

函数作为返回值:

  • 逻辑连续,当内部函数被调用时,不脱离当前的逻辑
3.3 装饰器 3.3.1 需求引入

先看需求:对进入后台和删除订单两个功能增加权限验证

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
  1. 定义:在不改变原函数的调用以及内部代码情况下,为其增加新功能的函数
  2. 语法:
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)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5443184.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-11
下一篇 2022-12-11

发表评论

登录后才能评论

评论列表(0条)

保存