10_Python的函数function
1.函数的概述
1.函数是可以重复执行的语句块且可以重复调用,函数封装了可重复执行的语句提高了语句的可重复性
2.函数的参数和返回值的作用流程图: https://www.processon.com/view/link/5ec2a0940791290fe06ea284
3.面向过程开发流程图: https://www.processon.com/view/link/5ec32177e0b34d5f26172759
4.函数定于语句的完整语法
[...]
[@装饰器2]
[@装饰器1]
def 函数名([位置形参], [*元组形参], [命名关键字形参], [**字典形参]):
"文档字符串"
语句块
2.函数的定义def
语法概述:
def 函数名(参数列表):
语句块(代码块)
语法说明:
1.函数名就是语句块的名称是变量,它在创建时绑定一个函数,命名规则与变量名命名规则相同必须为标识符
2.函数名是一个变量不要轻易对其赋值,函数的自己的命字空间,在函数内部可以访问外部的变量,但外部的语句不能访问函数内部的变量
3.函数内部有独立的运行空间,函数外部不能访问函数内的变量;函数如果不需要传入参数,则参数列表可以为空
4.语句部分不能为空,如果为空需要填充pass语句;没有返回值叫过程有返回值叫函数,没有返回值的函数默认返回None


- def say_hello():
- print("hello world!")
- print("hello coco!")
示例1:


- def number_int(num):
- print('在函数内部 %d 对应的内存地址是 %d' % (num, id(num)))
- # pass
- # 1> 定义一个字符串变量
- result = "hello"
- print("函数要返回数据的内存地址是 %d" % id(result))
- # 2> 将字符串变量返回,返回的数据的引用而不是数据本身
- return result
- # 定义一个数字变量
- a = 10
- # 数据的地址本质上就是一个数字
- print("a变量保存的数据内存地址是 %d" % id(a))
- # 1.调用函数时,本质上传递的是实参保存的数据引用而不是实参保存的数据
- # 2.如果函数有返回值但没有定义变量接收程序不会报错但是无法获得返回结果
- r_int = number_int(a)
- print("%s 的内存地址是 %d" % (r_int, id(r_int)))
示例2:


- def f1(lst=[]):
- print("f1函数被调用")
- f1()
- f1 = None
- f1() # 出错,f1 绑定的是None
示例3: 函数名是变量


- def f1():
- print("hello")
- def f2():
- print("world")
- f1, f2 = f2, f1
- f1() # world
示例4: 函数名是变量
3.函数的调用
语法概述: 函数名(实际调用传递参数) # 实际调用传递参数简称"实参"
语法说明: 函数调用是一个表达式,如果函数需要返回其它的对象则需要用到return语句;如果没有return语句,函数执行完毕后返回None对象


- def print_line(char, times):
- """打印单行分割线
- :param char: 分割线使用的分割字符
- :param times: 分割线重复的次数
- """
- print(char * times)
- def print_lines(char=input("要打印的分割线标记:"), times=int(input("每行打印分割线的标记数:"))):
- """打印指定行数的分割线
- :param char: 指定分割线的分割字符
- :param times: 指定分割线的重复次数
- """
- i = 0
- # 指定分割线的打印行数
- j = int(input("输入分割线行数: "))
- while i < j:
- print_line(char, times)
- i += 1
- print_lines()
示例1:


- # 函数的前向引用
- name = "zhangsan" # 第1步
- def name1(): # 第2步
- name = "lisi" # 第4.1步
- def lisi(): # 第4.2步
- nonlocal name # 第4.3.1步 nonlocal指定上一级的变量
- name = "wangwu" # 第4.3.2步
- lisi() # 第4.3步
- print(name) # 第4.4步 1.打印wangwu
- name2() # 第4.5步
- def name2(): # 第3步
- print(name) # 第4.5.1步 2.打印zhangsan
- name1() # 第4步
- print(name) # 第5步 3.打印zhangsan
示例2:
4.函数的返回return
语法概述: return [表达式] # []代表其中的内容可以省略
语法说明:
1.return语句后跟的表达式可以省略,省略后相当于return None
2.如果函数内没有return语句,则函数执行完最后一条语句后返回None相当于在最后加了一条return None语句
3.return用于函数中结束当前函数的执行,返回到调用函数的地方同时返回一个对象的引用关系
4.函数可以返回另一个函数也可以做为另一个函数的返回值


- # 带参函数定义和调用及return返回值
- def add(a, b):
- return a + b
- ret = add(1,2)
- print(ret) #
示例1:


- def sum_2_num(num1,num2):
- """对两个数字的求和"""
- result = num1 + num2
- # 可以使用返回值,告诉调用函数一方的计算结果
- return result
- # 注意: return 就表示返回,下方的代码不会被执行到
- # 可以使用变量,来接收函数执行的返回结果
- sum_result = sum_2_num(10,20)
- print("计算结果是: %d" % sum_result)
示例2:


- def measure():
- """测量温度和湿度"""
- print("测量开始")
- temp = 39
- wetness = 50
- print("测量结束")
- # 如果函数返回的类型是元组,小括号可以省略,元组可以一次返回多个值
- # return (temp, wetness)
- return temp, wetness
- result = measure()
- print(result)
- # 需要单独处理温度或湿度
- print(result[0])
- print(result[1])
- # 如果函数的返回类型是元组,同时希望单独处理元组中的元素,可以使用多个变量,一次接收函数的返回结果
- # 使用多个变量接收结果时,变量的个数应该和元组中元素的个数保持一致
- gl_temp, gl_wetness = measure()
- print(gl_temp)
- print(gl_wetness)
示例3:


- def get_op():
- s = input("请输入您的操作: ")
- if s == "求最大":
- return max
- elif s == '求最小':
- return min
- elif s == '求和':
- return sum
- L = [2,4,6,8,10]
- print(L)
- f = get_op()
- print(f(L))
示例4:
5.函数的嵌套
- # def语句的作用是用来创建一个函数
- def get_func(value):
- if value == 1:
- # def语句可以写在函数内部,在函数执行时可以动态创建一个函数
- def myadd(x, y):
- return x + y
- return myadd
- elif value == 2:
- def mysub(x, y):
- return x - y
- return mysub
- fx = get_func(1)
- print(fx(400, 300)) #
- fx = get_func(2)
- print(fx(400, 300)) #
6.函数的参数
1.不可变和可变的参数


- def demo(num, num_list): # 形参:被调用时分配内存,调用结束后释放,函数内部有效,函数内部作为变量使用
- print("函数内部的代码")
- num = 100 # 在函数内部针对形参使用赋值语句,仅会修改内部形参变量,不会修改外部实参变量
- num_list = [1, 2, 3]
- print(num, num_list) # 100 [1, 2, 3]
- print("函数执行完成")
- gl_num = 10
- gl_list = [4, 5, 6]
- demo(gl_num, gl_list) # 实参
- print(gl_num, gl_list) # 10 [4, 5, 6]
示例:
2.函数内部通过方法修改可变参数


- def demo(num_list):
- print("函数内部的代码")
- # 使用方法修改列表的内容,会影响外部的数据
- num_list.append(9)
- print(num_list)
- print("函数执行完成")
- gl_list = [1, 2]
- demo(gl_list)
- print(gl_list)
示例1:


- def demo(num, num_list):
- print("函数开始")
- # num = num + num
- num += num
- # num_list = num_list + num_list # 列表变量使用+不会做相加再赋值的操作!
- num_list += num_list # +=本质上是在调用列表的 extend 方法
- # num_list.extend(num_list)
- print(num, num_list)
- print("函数执行完成")
- gl_num = 9
- gl_list = [1, 2]
- demo(gl_num, gl_list)
- print(gl_num, gl_list)
示例2:
3.一个函数可以作为另一个函数的实参参数传递


- def f1():
- print("f1函数被调用")
- def f2():
- print("f2函数被调用")
- def fx(fn):
- print("fn绑定的函数是:", fn)
- # 在fx内调用fn绑定的函数
- fn()
- fx(f1) # 调用fx,把f1作为实参传数
- fx(f2) # 调用fx,把f2作为实参传数
- """执行结果
- fn绑定的函数是: <function f1 at 0x0000000004719C80>
- f1函数被调用
- fn绑定的函数是: <function f2 at 0x0000000004719BF8>
- f2函数被调用
- """
示例1:


- def goodbye(L):
- for x in L:
- print("再见:", x, end=' ')
- def hello(L):
- for x in L:
- print("欢迎:", x, end=' ')
- def fx(fn, L):
- print("fx被调用", end=' ')
- fn(L)
- fx(hello, ['Coco', 'Angels']) # fx被调用 欢迎: Coco 欢迎: Angels
- fx(goodbye, ['可可', '天使']) # fx被调用 再见: 可可 再见: 天使
示例2:
4.函数的参数传递--实参
1.位置传参: 实际调用参数(实参)的对应关系与形式参数(形参)的对应关系是按位置来依次对应的


- def fx(a, b, c):
- pass
- # ^ ^ ^
- fx( 1, 2, 3)
示例:
2.序列传参(动态传传递位置参数): 在函数调用过程中,用*将序列拆解后按位置进行传递的传参方式,实参和形参通过序列传递和匹配


- def fx(a, b, c):
- pass
- s1 = [11, 22, 33] # 列表
- fx(*s1) # 将s1序列拆解后按位置传入fx中
示例:
3.关键字传参: 传参时按着形参的名称给形参赋值,即实参和形参按名称进行匹配,传参时可以不按位置进行匹配


- def fx(a, b, c):
- pass
- fx(b=22, c=33, a=11) # 11->a, 22->b, 33->c
示例:
4.字典关键字传参(动态传递关键字参数): 用**拆解字典后再进行关键字传参,字典的键名和形参名必须一致,键名为字符串并且要在形参中存在


- def fx(a, b, c):
- pass
- d = {'c': 33, 'b': 22, 'a': 11}
- fx(**d) # 拆解字内再依次按关键字传参
示例:
5.综合传参: 函数的传参方式在能确定形参能唯一匹配到相应实参的情况下可以任意组合


- def fx(a, b, c, d, e, f):
- pass
- fx(10, *[20, 30], e=50, **{'d':40, 'f':60}) # 通常位置传参和序列传参先传递,其次是关键字传参和字典关键字传参
- fx(e=50, **{'d':40, 'f':60}, 10, *[20, 30]) # 错误的做法
示例:
5.函数的参数接收--形参
形参概述: 缺省参数,位置形参,星号元组形参,命名关键字形参,双星号字典形参可以混合使用
1.函数的缺省参数
语法概述:
def 函数名(形参名1=默认实参1, 形参名2=默认实参2, ...): pass
缺省参数必须自右至左依次存在,缺省参数可以有0个,1个,多个,甚至全部都有缺省参数
缺省参数的绑定对象存在于函数内,同函数的生命周期一致


- def info(name, age=1, address="不详"):
- print("我叫", name, '我今年:', age, '岁, 家庭住址:', address)
- info("Coco", 30, "中国")
- info("Angels", 18)
- info("Cat")
示例1:


- # 缺省参数的绑定对象存在于函数内,同函数的生命周期一致
- def fun(a, lst=[]):
- lst.append(a)
- print(lst)
- l = [1, 2, 3]
- fun(4, l) # [1, 2, 3, 4]
- print(l) # [1, 2, 3, 4]
- fun(5, l) # [1, 2, 3, 4, 5]
- print(l) # [1, 2, 3, 4, 5]
- fun(6) # [6]
- fun(7) # [6, 7]
- print(l) # [1, 2, 3, 4, 5]
示例2:


- def print_info(name, gender=True):
- """
- :param name: 班上同学的姓名
- :param gender:True 男生 False 女生
- """
- gender_test = "男生"
- if not gender:
- gender_test = "女生"
- print("%s 是 %s" % (name, gender_test))
- # 在指定缺省参数的默认值时,应该使用最常见的值作为默认值
- print_info("小明")
- print_info("小美", False)
示例3:
2.函数的形参定义方式
1.位置形参
def 函数名(形参名1, 形参名2, ....): pass
2.星号元组形参(动态接收位置参数)
def 函数名(*元组形参名): pass # 收集多余的位置传参,元祖形参名通常叫 args
3.命名关键字形参
# 所有的命名关键字形参都强制调用者采用关键字传参或字典关键字传参的方式传递
def 函数名(*, 命名关键字形参): pass
def 函数名(*args, 命名关键字形参): pass
4.双星号字典形参(动态接收关键字参数)
def 函数名(**字典形参名): pass # 收集多余的关键字传参,字典形参名通常叫 kwargs


- def myfun2(a, b, *args, c, **kwargs):
- print(a, b, args, c, kwargs) # 10 20 (30, 40, 'a', 'b') 50 {'d': 60}
- myfun2(10, 20, 30, 40, *'ab',**{'d': 60}, c=50)
示例1:


- # *作为标识符存在,标识着*右面的所有参数都是关键字参数
- # *args,*后面加元组形参名,表示可以接收多个位置参数
- def myfun(a, *, k):
- print('a=', a, 'k=', k)
- # k强制使用关键字传参
- myfun(10, k=20) # a= 10 k= 20
- # 字典关键字传参
- myfun(100, **{'k':200}) # a= 100 k= 200
示例2:
3.函数形参接收顺序
函数参数自左至右的顺序为: 位置形参-->星号元组形参-->命名关键字形参-->双星号字典形参


- def fn(a, b, *args, c, **kwargs):
- print(a, b, args, c, kwargs)
- # 100 200 (300, 400, 'A', 'B') 100 {'d': 'D'}
- fn(100, 200, 300, 400,*"AB", **{'d':"D"}, c=100)
- # 100 200 (300, 400, 'A', 'B') C {'d': 'D', 'e': 100}
- fn(100, 200, 300, 400,*"AB", **{'d':"D", "c":"C"}, e=100)
示例1:


- # 可以接收任意位置传参和关键字传参的函数
- def fn(*args, **kwargs): pass
示例2:
7.函数的文档字符串
1.语法概述:
def 函数名(参数列表):
"""函数的文档字符串"""
函数语句块
2.语法说明:
1.文档字符串通常来用说明本函数的功能和使用方法;在交互模式下,输入help(函数名)可以查看函数的"文档字符串"
2.插入多行文档字符串方法是在pycharm中点击函数名或者def定义语句选则: Insert documentation string stub
3.函数的__doc__属性:
函数内第一次末赋值给任何变量的字符串是此函数的文档字符串,此字符串会自动赋值给函数的__doc__属性
示例:
def func():
"""这是一个文档字符串"""
pass
print(func.__doc__) # 这是一个文档字符串
8.内置获取属性函数dir
语法概述: dir([对象]) # 返回一个字符串列表
语法说明:
1.如果没有参数调用则返回当前作用域内的所有变量的列表,如果给定一个对象作为参数则返回这个对象的所有变量的列表
2.对于模块,返回这个模块的全部属性
3.对于一个类对象,返回类对象的所有变量,并递归基类对象的所有属性;对于其它对象返回所有变量,类变量和基类变量
9.内置获取变量函数 globals, locals 与声明语句 global, nonlocal
globals() # 返回全局作用内变量的字典
locals() # 返回局部作用域内变量的字典
global
语法概述: global 变量名
语法说明:
1.global语句声明的一个或多个变量,这些变量的作用域为模块级的作用域的变量,即全局变量
2.global全局声明将赋值变量映射到模块文件内部的作用域,global变量列表里的变量名不能出现在此作用域内的形参列表里
3.全局变量在函数内部不经过声明就可以直接访问,但是在函数内部被赋值,则必须经过全局声明否则会被认为是局部变量
4.不能先声明局部变量,再用global声明为全局变量,此做法不附合规则


- # 全局变量不能出现在此作用域内的形参列表里
- def fun1(a):
- global a
- """会报错
- File "<ipython-input-6-d92ed22bfd1d>", line 5
- SyntaxError: name 'a' is parameter and global
- """
示例:
nonlocal
语法概述: nonlocal 变量名
语法说明:
1.nonloca声明的变量不是局部变量,也不是全局变量,而是外部嵌套函数内的变量
2.nonlocal语句只能在被嵌套函数内部进行使用,nonlocal语句的变量列表的变量名不能出现在此函数的参数列表里
3.访问nonlocal变量将对外部嵌套函数作用域内的变量进行操作
4.当有两层或两层以上函数嵌套时,访问nonlocal变量只对最近一层的变量进行操作


- def f1():
- v =100
- def f2():
- v = 200
- def f3():
- nonlocal v
- v += 1 # 此时只对f2的v进行操作
- f3()
- f2()
示例:
10.内置执行函数eval和exec
eval
语法概述: eval(source, globals=None, locals=None)
语法说明: 把一个字符串当成一个表达式执行,返回表达式执行后的结果


- x = 100
- y = 200
- s = "x+y"
- v = eval(s)
- print(a)
- print(eval("x+y", {'x':10, 'y':20})) #
- print(eval("x+y", {'x':10, 'y':20}, {'x':1, 'y':2})) #
- print(eval("x+y", {'x':10, 'y':20}, {'x':1})) #
示例:
eval函数的危险操作
# 在开发是不要直接使用eval直接转换input输入的结果
print(eval(input("请输入:"))) # 执行成功返回0, 执行失败返回错误信息
当输入"__import__('os').system('ls')"时可以列表出当前目录
当输入"__import__('os').system('rm *')"时可以删除当前目录下的内容
exec
语法概述: exec(source, globals=None, local=None)
语法说明: 把一个字符串当成程序来执行


- s = 'x=100; print("hello"); x += 1; print(x)'
- print(s)
- exec(s)
- 执行结果:
- x=100; print("hello"); x += 1; print(x)
- hello
- 101
示例:
11.内置迭代工具函数zip和enumerate
zip和enumerate都是用来生成一个个性化的可迭代对象
zip
语法概述: zip(iter1[, iter2, ...])
语法说明: 返回一个用于生成一个元组的zip对象,元组的个数是由最小的可迭代对象决定,元组内容是可迭代对象iter1和iter2中元素的组合


- print(list(zip(("a", "b", "c"), (1, 2, 3)))) # [('a', 1), ('b', 2), ('c', 3)]
- # max和zip高级连用
- age = {"age": 10,
- "age1": 20,
- "age2": 30,
- "age3": 12,
- }
- print(max(zip(age.values(), age.keys()))) # [30, 'age2']
示例:
enumerate
语法概述: enumerate(iterable,start)
语法说明: 生成带索引的枚举对象,返回的迭代类型为索引-键值对(index-value),默认索引从零开始,也可以用s


- dic = {"name": "zhangsan", "age": 18}
- for item in enumerate(dic, 1):
- print(item, end=' ') # (1, 'name') (2, 'age')
示例:
12.函数式编程
概述: 函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量;任意一个函数,
只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用;而允许使用变量的程序设计语言,由
于函数内部的变量状态不确定,同样的输入可能得到不同的输出,因此这种函数是有副作用的;函数式编程的一
个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数;Python对函数式编程提供部分支
持,由于Python允许使用变量,因此Python不是纯函数式编程语言
定义:
1.是指用一系列函数解决问题
2.不可变: 不用变量保存状态,不修改变量
3.函数即变量: 函数名可以当参数传递,返回值可以是函数名
4.尾调用: 在函数的最后一步调用另外一个函数


- # 非函数式
- a = 1
- def funck():
- global a
- a += 1
- return a
- print(funck())
- # 函数式
- b = 1
- def funck1(b):
- return b+1
- print(funck1(b))
示例:
函数本身优势:
1.函数本身是对象,可以赋值给变量,赋值后变量绑定函数
2.允许将函数作为实参传入另一个函数,允许函数返回一个函数
3.函数是一等公民,优先使用函数解决问题
函数式编程的优势:
1.每一个函数完成细小的功能,一系列函数的任意组合可以解决大的问题
2.函数仅接收输入并产生输出,不会影响其它全局变量的状态


- # 求 1+ 2 + 3 + ...... + n 的和
- print(sum(range(n+1)))
示例:
13.匿名函数lambda表达式
语法概述: lambda 形参1, 形参2, 形参n: 表达式 # 创建一个匿名函数对象,同def类似但不提供函数名
语法说明:
1.lambda 只是一个表达式,它用来创建一个函数对象
2.lambda 表达式调用时,先执行冒号后的表达式,并返回表达式的结果的引用
3.lambda 表达式创建的函数只能包含一条表达式
4.lambda 比函数简单,且可以随时创建和销毁,有利于减少程序的偶合度


- def myadd(x, y):
- return x + y
- # 可以改写为
- myadd = lambda x, y: x + y
- print(myadd(100, 200)) #
- print(myadd("ABC", "")) # ABC100
示例1:


- fx = lambda x: (x**2 + 1)%5 == 0
- print(fx(4)) # False
- print(fx(3)) # True
- mymax = lambda x, y: x if x > y else y
- print(mymax(55, 63)) #
示例2:


- def fx(f, x, y):
- r = f(x, y)
- print(r)
- fx((lambda a, b: a + b), 100, 200) #
- fx((lambda x, y: x ** y), 3, 4) #
示例3:
14.递归函数recursion
1.语法概述
1.递归必须有一个明确的结束条件,几乎所有的递归都能用循环来代替,递归是函数直接或间接的调用自身,使用时优先考虑尾递归
2.每次进入更深一层递归时,问题规模相比上一次递归应有所减少,递归效率不高,递归层数过多会导致栈(stack)溢出
3.每进入一个函数调用就会增加一层栈帧,每当函数返回,就会减少一层栈帧,栈的大小不是无限的,所以递归次数过多会导致栈溢出
4.递归的优缺点和实现技巧
优点: 递归可以把问题简单化,让路径更为清晰,代码更为简洁
缺点: 递归因系统环境影响大,当递归深度太大时,可以会得到不可预知的结果
技巧: 写递归函数技巧就是先假设函数已经实现
5.递归函数流程图: https://www.processon.com/view/link/5ec318b0e0b34d5f261723c3
2.非尾递归


- def cal(seq):
- print(seq)
- if len(seq) == 1:
- return seq[0]
- first, second, *args = seq
- seq[0] = first + second
- seq.pop(1)
- return cal(seq)
- x = cal([i for i in range(100)])
- print(x)
示例:
3.尾递归: 上一层函数已经结束,所以不保留上一层栈帧,优化程序


- def cal(seq):
- print(seq)
- if len(seq) == 1:
- return seq[0]
- first, second, *args = seq
- seq[0] = first + second
- seq.pop(1)
- return cal(seq)
- x = cal([i for i in range(100)])
- print(x)
示例:
4.递归深度-->默认997


- import sys
- sys.setrecursionlimit(1500) # 可以调整递归深度,但是不一定能跑到
- def func(n):
- print("递归深度%s" % n)
- func(n+1)
- func(1)
示例:
5.递归函数的实现方法


- # 求:100 + 99 + 98 + 97 + ..... + 1 的和
- # 分析: 先假设 mysum(x) 已经完成,且能求x + (x-1) + .. +1的和
- def mysum(x):
- # 选判断终止条件
- if x == 1:
- return 1
- return x + mysum(x - 1)
- print(mysum(100)) #
示例1:


- # 运用函数的尾递归计算[1, 100]中能被3和7整除的数的个数和求和
- def func(start, end, a=0, b=0):
- if start > end:
- return a, b
- if start % 3 == 0 and start % 7 == 0:
- a += 1
- b += start
- ret = func(start+1, end, a, b)
- return ret
- res = func(1, 100)
- print("1到100中能被3和7整除的数的个数%s个,"
- "满足要求的数的求和是%s" % (res[0], res[1]))
示例2:


- # 借钱真难
- import time
- person_list = ["张三", "李四", "王五", "coco"]
- def borrow_money(preson_list):
- print("=" * 60)
- if len(person_list) == 0:
- return "没人愿意借给你" # 递归出口1
- person = person_list.pop(0)
- if person == "coco":
- return "%s让你去找他,他借给你" % person # 递归出口2
- print("%s有钱借我100元吗?" % person)
- print("%s回答道: 我没有钱,我帮你问问%s" % (person, person_list[0]))
- time.sleep(2)
- res = borrow_money(person_list)
- print("%s问的结果是: %s" % (person, res))
- time.sleep(2)
- return res
- borrow_money(person_list)
示例3:


- # 递归遍历目录树形结构
- import os
- file_path = r"G:\PycharmProjects\01_Python基础"
- def read(file_path, n):
- files = os.listdir(file_path) # 获取当前文件夹中的所有文件
- for file in files: # 遍历文件夹中的本层文件名
- file_d = os.path.join(file_path, file) # 取当前的绝对路径
- if os.path.isdir(file_d): # 判断是否是文件夹
- # 打印带递归深度的文件夹,递归深度代表着缩进程度
- print('\t'*n, file)
- read(file_d, n+1) # 是文件夹则递归遍历
- else:
- # 打印带递归深度的文件,递归深度代表着缩进程度
- print("\t"*n, file) # 递归出口
- pass
- read(file_path, 0)
示例4:
15.高阶函数概述与常用的内置高阶函数(map,filter,sorted,reduce)
高阶函数概述: 满足下列条件中的一个的函数即为高阶函数
1.一个函数的形参可以接收另一个或多个函数作为参数
2.一个函数的返回值为另外一个函数,若返回值为该函数本身则为递归


- def test11(): # 是高阶函数,返回值包含函数
- print("")
- return test22(test11)
- def test22(n): # 是高阶函数,参数是函数
- print(n)
- def test33(): # 是高阶函数,返回值是函数
- print("")
- return test11()
- test22(test33())
示例:
映射高阶函数 map
语法概述: map(func, *iterables) # 参数一执行方法,参数二为一个或多个可迭代对象
语法说明:
1.用函数对可迭代对象中的每一个元素作为参数计算出新的可迭代对象(可以使用一次的内存地址)
2.当最短的一个可迭代对象不再提供数据时,此可迭代对象生成结束,得到的这个迭代器列表的元素个数和位置与原来一样
3.map函数是一个生成器函数,返回一个生成器对象


- # 生成一个可迭代对象,要求此可迭代对象可以生成1~9自然数的平方 1, 4, 9, 16, .... 81
- def power2(x):
- return x**2
- for x in map(power2, range(1, 10)):
- print(x)
- # 求数据1 + 4 + 9 + 16 + ... + 81的和
- print(sum(map(power2, range(1, 10))))
示例1:


- # 生成一个可迭代对象,要求此可迭代对象生成
- # 1**4, 2**3, 3**2, 4**1
- # 1 8 9 4
- for x in map(pow, [1,2,3,4], [4,3,2,1]):
- print(x)
示例2:


- # 模拟map函数实现
- num_1 = [1, 3, 5, 7, 9]
- def map_test(func, array):
- ret = []
- for i in array:
- res = func(i)
- ret.append(res)
- return ret
- print(map_test(lambda x:x+1, num_1))
示例3:
过滤高阶函数 filter
语法概述: filter(func, iterable) # 参数一为判断函数,参数二为一个可迭代对象
语法说明:
1.筛选可迭代对象iterable中的数据,返回一个可迭代对象,此可迭代对象将对iterable进行筛选
2.函数将对iterable中的每个元素进行求值,返回False时将此数据丢弃,返回True,则保留此数据


- # isodd函数判断x是否为奇数,是奇数返回True
- def isodd(x):
- return x % 2 == 1
- # 打印10以内的奇数:
- for x in filter(isodd, range(10)):
- print(x)
- # 生成10以内所有偶数的列表,用filter实现
- L = [x for x in filter(lambda x: x%2==0, range(10))]
示例1:


- # 模拟 filter 函数实现
- mv_list = ["阮小二", "阮小五", "阮小七", "武大郎"]
- def test1(func, array):
- ret = []
- for i in array:
- if not func(i):
- ret.append(i)
- return ret
- res = test1(lambda n:n.endswith("阮"), mv_list)
- print(res)
示例2:


- # map 返回执行后的结果值-->得到结果
- print(list(map(lambda x: x%2==0, range(4)))) # [True, False, True, False]
- # filter 返回满足条件的结果值-->清洗数据
- print(list(filter(lambda x: x%2==0, range(4)))) # [0, 2]
示例4: map与filter的区别
排序高阶函数 sorted
语法概述: sorted(iterable, key=None, reverse=False) # 将原可迭代对象的数据进行排序,生成排序后的列表
语法说明:
iterable 可迭代对象
key 绑定函数,此函数用来提供一个排序的依据
reverse 排序规则: reverse = True 降序; reverse = False 升序默认


- L = [5, -2, -4, 0, 3, 1]
- L2 = sorted(L) # L2 = [-4, -2, 0, 1, 3, 5]
- L3 = sorted(L, reverse=True) # L3 = [5, 3, 1, 0, -2, -4]
示例1:


- L = [5, -2, -4, 0, 3, 1]
- # 依据: abs(5), abs(-2), abs(-4), abs(0), abs(3), abs(1)
- L4 = sorted(L, key=abs) # L4 =[0, 1, -2, 3, -4, 5]
示例2:


- names = ['Tom', 'Jerry', 'Spike', 'Tyke']
- L = sorted(names) # L = ['Jerry', 'Spike', 'Tom', 'Tyke']
示例3:


- # 根据名字的长度进行排序
- L2 = sorted(names, key=len) # L2 = ['Tom', 'Tyke', 'Jerry', 'Spike']
示例4:


- # 自定义排序规则
- def fr(s):
- return s[::-1] # 把name中的每一个字符串翻转
- L3 = sorted(names, key=fr) # L3 = ['Spike', 'Tyke', 'Tom', 'Jerry']
示例5:
迭代高阶函数 reduce
语法概述: reduce(function, iterable[, initializer]) # function函数,iterable可迭代对象,initializer可选初始参数
语法说明:
1.reduce函数会对参数序列中元素进行累积
2.reduce函数先将可迭代对象传递给function函数操作,得到的结果与第三个参数再传参给function函数操作,返回得到的最终结果


- from functools import reduce
- num1 = [1, 2, 3, 4, 5]
- def my_reduce(func, array, init=None):
- """模拟reduce函数原理"""
- if init is None:
- res = array.pop(0)
- else:
- res = init
- for i in array:
- res = func(res, i)
- return res
- msg = my_reduce(lambda i,j: i + j, num1, 10)
- print(msg) #
- # reduce函数遍历合并可迭代对象中的每个元素
- msg = reduce(lambda i, j: i + j, num1, 10)
- print(msg) #
示例:
16.闭包closure
闭包概述
1.将内嵌函数的语句和这些语句的执行环境打包在一起后,得到的函数对象称为闭包,作用就是让一个变量量能够常驻内存供后面的程序使用
2.如果一个内嵌函数访问了外部嵌套函数作用域内的变量,并且该内嵌函数可以在其定义环境外被执行,则这个内嵌函数就是闭包
3.闭包流程图: https://www.processon.com/view/link/5edb93f50791297145d51f8d
4.闭包必须满足以下三个条件:
1.必须有一个内嵌函数
2.内嵌函数必须引用外部函数中的变量
3.内嵌函数可以在其定义环境外被执行


- # 闭包:变量常驻内存,防止其他程序修改
- def func():
- name = "coco" # 常驻内存,func执行完毕后不会被回收
- def func1():
- # 在内层函数中调用了外层函数的变量叫闭包,可以让一个局部变量常驻内存
- print(name)
- print(func1.__closure__) # 返回一个cell的内存地址则是闭包,否则返回None
- return func1
- ret = func() # 如果没有闭包,func执行完成后name变量会被回收
- ret() # 执行func1()
示例1:


- def make_power(y):
- def fn(x):
- return x ** y
- return fn
- pow2 = make_power(2) # pow2 = fn(x) 携带变量y = 2
- print("5的平方是: ", pow2(5)) #
示例2:


- # 闭包的常见运用: 爬虫
- from urllib.request import urlopen
- def but():
- content = urlopen("http://www.xiaohua100.cn/index.html").read()
- def get_content():
- # 在函数内部使用了外部的变量
- return content
- return get_content
- fn = but() # 这个时候就开始加载校花100的内容
- # 后⾯面需要⽤用到这里面的内容就不需要在执行非常耗时的网络连接操作了
- content = fn() # 获取内容
- print(content)
- content2 = fn() # 重新获取内容
- print(content2)
示例3:
17.装饰器decorators
1.装饰器概述:
1.装饰器是一个函数,这个函数的主要作用是包装别一个函数或类,包装的目的是在不改变原函数名的情况下改变被包装对象的行为
2.函数装饰器是指装饰器是一个函数,传入的是一个函数,返回的也是一个函数;装饰器 = 高阶函数 + 函数嵌套 + 闭包
3.装饰器本质就是函数是为其他函数添加附加功能,装饰器是在被解释器自上而下解释时已经执行,而非调用时执行
4.装饰器原则: 不修改被修饰函数的源代码和调用方式
5.多个装饰器装饰函数是自下而上依次装饰的,所有被装饰的函数调用时会最先体现最外层装饰器的功能
@装饰器3 # 解释器最后装饰,被装饰的函数调用时最先执行其装饰的功能
@装饰器2 # 解释器其次装饰,被装饰的函数调用时其次执行其装饰的功能
@装饰器1 # 解释器最先装饰,被装饰的函数调用时最后执行其装饰的功能
def fn():
pass
2.装饰器原理


- def mydeco(fn): # 装饰器函数
- def fx():
- print("开始装饰hello函数")
- fn()
- print("结束装饰hello函数")
- return fx
- def hello(): # 被装饰的函数
- print("hello")
- # 此时将hello变量绑定在了mydeco返回的函数上
- hello = mydeco(hello)
- hello()
- """执行结果
- 开始装饰hello函数
- hello
- 结束装饰hello函数
- """
示例:
3.对一个没有参数和返回值的函数进行装饰


- def set_func(func):
- def call_func():
- print("权限验证1")
- print("权限验证2")
- func()
- return call_func
- @set_func # 等价于test1 = set_func(test1)
- def test1():
- print("this is test1 function")
- # ret = set_func(test1)
- # ret()
- test1()
- """执行结果
- 权限验证1
- 权限验证2
- this is test1 function
- """
示例:
4.对有参数但没有返回值的函数进行装饰


- def set_func(func):
- def call_func(num):
- print("权限验证1")
- print("权限验证2")
- func(num)
- return call_func
- @set_func # 等价于test1 = set_func(test1)
- def test1(num):
- print("this is test1 function---%s" % num)
- test1(100)
- """执行结果
- 权限验证1
- 权限验证2
- this is test1 function---100
- """
示例:
5.一个装饰器装饰多个函数


- def set_func(func):
- def call_func(num):
- print("权限验证1")
- print("权限验证2")
- func(num)
- return call_func
- @set_func # 等价于test1 = set_func(test1)
- def test1(num):
- print("this is test1 function---%s" % num)
- @set_func # 等价于test2 = set_func(test2)
- def test2(num):
- print("this is test2 function---%s" % num)
- test1(100)
- test1(200)
- """执行结果
- 权限验证1
- 权限验证2
- this is test1 function---100
- 权限验证1
- 权限验证2
- this is test2 function---100
- """
示例:
6.验证装饰器在调用函数之前已经被装饰


- def set_func(func):
- print("开始进行对函数%s的装饰" % func.__name__)
- def call_func(num):
- print("权限验证1")
- print("权限验证2")
- func(num)
- print("确认完成对函数%s的装饰" % func.__name__)
- return call_func
- @set_func # 等价于test1 = set_func(test1)
- def test1(num):
- print("this is test1 function---%s" % num)
- @set_func # 等价于test2 = set_func(test2)
- def test2(num):
- print("this is test2 function---%s" % num)
- """执行结果
- 开始进行对函数test1的装饰
- 确认完成对函数test1的装饰
- 开始进行对函数test2的装饰
- 确认完成对函数test2的装饰
- """
示例:
7.验证多个装饰器加载顺序和调用函数执行顺序


- def fun1(fn):
- print("解释器加载时,装饰器func1开始装饰")
- def fx():
- print("调用fun函数时,装饰器fun1开始装饰")
- fn()
- print("结束调用fun函数时,装饰器fun1结束装饰")
- return fx
- def fun2(fn):
- print("解释器加载时,装饰器func2开始装饰")
- def fx():
- print("调用fun函数时,装饰器fun2开始装饰")
- fn()
- print("结束调用fun函数时,装饰器fun2结束装饰")
- return fx
- def fun3(fn):
- print("解释器加载时,装饰器func3开始装饰")
- def fx():
- print("调用fun函数时,装饰器fun3开始装饰")
- fn()
- print("结束调用fun函数时,装饰器fun3结束装饰")
- return fx
- @fun3
- @fun2
- @fun1
- def fun():
- print("被装饰的函数fun")
- fun()
- """执行结果
- 解释器加载时,装饰器func1开始装饰
- 解释器加载时,装饰器func2开始装饰
- 解释器加载时,装饰器func3开始装饰
- 调用fun函数时,装饰器fun3开始装饰
- 调用fun函数时,装饰器fun2开始装饰
- 调用fun函数时,装饰器fun1开始装饰
- 被装饰的函数fun
- 结束调用fun函数时,装饰器fun1结束装饰
- 结束调用fun函数时,装饰器fun2结束装饰
- 结束调用fun函数时,装饰器fun3结束装饰
- """
示例:
8.装饰器的完整语法


- import time
- def timmer(func):
- def wrapper(*args, **kwargs):
- start_time = time.time()
- res = func(*args, **kwargs)
- stop_time = time.time()
- print("函数的运行时间是%s" % (stop_time - start_time))
- return res
- return wrapper
- @timmer # @timmer-->cal = timmer(cal)
- def cal(li):
- res = 0
- for i in li:
- time.sleep(0.1)
- res += 1
- return res
- res = cal(range(20)) # 就是在运行timmer的返回值wrapper
- print(res)
- """执行结果
- 函数的运行时间是2.0593690872192383
- 20
- """
示例:
9.带有参数的装饰器


- def set_level(level_num):
- def set_func(func):
- def call_func(*args, **kwargs):
- if level_num == 10:
- print("权限验证1")
- elif level_num == 20:
- print("权限验证2")
- return func(*args, **kwargs)
- return call_func
- return set_func
- @set_level(10) # 等价于test1 = set_func(test1)
- def test1(num):
- print("this is test1 function---%s" % num)
- @set_level(20) # 等价于test2 = set_func(test2)
- def test2(num):
- print("this is test2 function---%s" % num)
- test1(100)
- test2(200)
- """执行结果
- 权限验证1
- this is test1 function---100
- 权限验证2
- this is test2 function---200
- """
示例:
18.迭代器Iterator
1.迭代器概述
1.迭代器是访问可迭代对象的一种方式,用迭代器可以访问可迭代对象
2.迭代器是指iter(可迭代对象)返回的对象,可以用next(it)函数获取可迭代对象的数据
3.迭代器只能往前取值不会后退,用iter函数可以返回一个可迭代对象的迭代器
4.迭代器特点: 节省内存, 惰性机制(不到最后不拿值), 只前进不后退
2.迭代器函数iter和next
iter(iterable): 从可迭代对象中返回一个迭代器,iterable必须是能提供一个迭代器的对象
next(iterator): 从迭代器iterator中获取下一个记录,如果无法获取下一条记录,则触发StopIterator异常


- L = [2, 3, 5, 7]
- # 确定L列表是可迭代对象
- print("__iter__" in dir(L)) # True
- # 确定L列表不是迭代器
- print("__next__" in dir(L)) # False
- it = iter(L) # 用iter返回一个迭代器用it绑定
- # 确定it对象是可迭代对象
- print("__iter__" in dir(it)) # True
- # 确定it对象是迭代器
- print("__next__" in dir(it)) # True
- # 用next(it)用迭代器来获取L中的元素
- next(it) #
- next(it) #
- next(it) #
- next(it) #
- next(it) # StopIteration #通知next调用者,已无数据
- # 用迭代器获取 range对象的数据
- it = iter(range(1, 10, 3))
- next(it) #
- next(it) #
- next(it) #
- next(it) # StopIteration
示例1:


- list1 = [1, 2, 3, 4]
- for i in list1: # 本质在调用列表调内置的__iter__方法把列表转换成可迭代对象再调用__next__
- print(i)
- list2 = list1.__iter__() # # 遵循迭代器协议生成可迭代对象
- print(list2.__next__())
- print(list2.__next__())
- print(list2.__next__())
- print(next(list2)) # next--->就是在调用list1.__next__()取迭代器的值
示例2:


- # 使用while循环+迭代器来模拟for循环,for循环的本质就是用迭代器去拿可迭代对象的数据
- lst = [1,2,3]
- lst_iter = lst.__iter__()
- while True:
- try:
- i = lst_iter.__next__()
- print(i)
- except StopIteration:
- break
示例3:
19.迭代器原理剖析
迭代器协议:
1.迭代器议是指对象能够使用next函数获取下一项数据, 在没有下一项数据时触发一个StopIteration异常来终止迭代的约定
2.迭代器协议的实现方法: 在类内需要定义 __next__(self) 方法来实现迭代器协议
3.迭代器原理剖析流程图: https://www.processon.com/view/link/5ee1bbcde401fd1fd2886be9
4.语法形式:
class MyIterator:
def __next__(self):
迭代器协议
return 数据
可迭代对象概述:
1.可迭代对象是指能用 iter(obj) 函数返回迭代器的实例对象
2.可迭代对象的内部要定义 __iter__(self) 方法来返回迭代器对象


- # for 循环内部迭代器原理剖析
- import time
- from collections import Iterable
- from collections import Iterator
- class Classmate(object):
- def __init__(self):
- self.names = list()
- self.current_num = 0
- def add(self, name):
- self.names.append(name)
- # 如果想要一个对象称为一个可迭代对象,即可以使用for,那么必须实现__iter__方法
- def __iter__(self):
- return self
- def __next__(self):
- if self.current_num < len(self.names): # 避免超出索引下标
- ret = self.names[self.current_num]
- self.current_num += 1
- return ret
- else:
- raise StopIteration # 取完值后主动抛出异常停止迭代
- classmate = Classmate()
- classmate.add("张三")
- classmate.add("李四")
- classmate.add("王五")
- print("判断classmate是否是可以迭代的对象: ", isinstance(classmate, Iterable))
- classmate_iterator = iter(classmate)
- print("判断classmate_iterator是否是迭代器: ", isinstance(classmate_iterator, Iterator))
- print(next(classmate_iterator))
- for name in classmate:
- print(name)
- time.sleep(1)
示例1:


- # 迭代器实现fibonacci数列-->存储生产数列的方法,节省存储资源
- class FibIterator(object):
- """斐波那契数列迭代器"""
- def __init__(self, n):
- """
- :param n: int, 指明生成数列的前n个数
- """
- self.n = n
- # current用来保存当前生成到数列中的第几个数了
- self.current = 0
- # num1用来保存前前一个数,初始值为数列中的第一个数0
- self.num1 = 0
- # num2用来保存前一个数,初始值为数列中的第二个数1
- self.num2 = 1
- def __next__(self):
- """被next()函数调用来获取下一个数"""
- if self.current < self.n:
- num = self.num1
- self.num1, self.num2 = self.num2, self.num1 + self.num2
- self.current += 1
- return num
- else:
- raise StopIteration
- def __iter__(self):
- """迭代器的__iter__返回自身即可"""
- return self
- if __name__ == "__main__":
- fib = FibIterator(10)
- for num in fib:
- print(num, end=" ") # 0 1 1 2 3 5 8 13 21 34
- # 除了for循环能接收可迭代对象,list,tuple等也能接收
- print() # 打印一个换行
- li = list(FibIterator(15))
- print(li) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
- tp = tuple(FibIterator(6))
- print(tp) # (0, 1, 1, 2, 3, 5)
示例2:


- class MyList:
- """定义一个容器类,用于存储任意类型的数据,其内部的存储方式用list实现"""
- def __init__(self, iterable):
- self.data = [x for x in iterable]
- def __repr__(self):
- return "MyList(%s)" % self.data
- def __iter__(self):
- """此方法把MyList类型的对象做为可迭代对象使用,此方法需要返回迭代器"""
- # return 迭代器
- return MyListIterator(self.data)
- class MyListIterator:
- """此类定义一个迭代器,用于生成能够访问MyList对象的迭代器"""
- def __init__(self, lst_data):
- print("迭代器已经创建")
- self.data = lst_data # 绑定要访问的数据列表
- self.cur_pos = 0 # 设置迭代器的起始位置为0
- def __next__(self):
- """此方法访问可迭代对象的数据,如果没有数据则触发StopIteration异常来通知调用者停止迭代即迭代器协议"""
- # 判定索引是否越界,如果以越界则触发异常停止迭代
- if self.cur_pos >= len(self.data):
- raise StopIteration
- index = self.cur_pos
- self.cur_pos += 1 # 将当前位置向后移动准备下次获取
- return self.data[index] # 返回当前位置的数据
- myl = MyList([1, 2, 3, 4, 5])
- for i in myl:
- print(i)
示例3:


- # 改写示例3完善迭代器
- class MyList:
- """定义一个容器类,用于存储任意类型的数据,其内部的存储方式用list实现"""
- def __init__(self, iterable):
- self.data = [x for x in iterable]
- def __repr__(self):
- return "MyList(%s)" % self.data
- def __iter__(self):
- """此方法把MyList类型的对象做为可迭代对象使用,此方法需要返回迭代器"""
- self.cur_pos = 0 # 设置迭代器的起始位置为0
- # return 迭代器
- return self
- def __next__(self):
- """此方法访问可迭代对象的数据,如果没有数据则触发StopIteration异常来通知调用者停止迭代即迭代器协议"""
- # 判定索引是否越界,如果以越界则触发异常停止迭代
- if self.cur_pos >= len(self.data):
- raise StopIteration
- index = self.cur_pos
- self.cur_pos += 1 # 将当前位置向后移动准备下次获取
- return self.data[index] # 返回当前位置的数据
- myl = MyList([1, 2, 3, 4, 5])
- it = iter(myl)
- while True:
- try:
- x = next(it)
- print(x)
- except StopIteration:
- break
示例4:
20.生成器Generator
1.生成器概述:
1.生成器是能够动态提供数据的对象,生成器对象也是可迭代对象
2.生成器有两种: 生成器函数,生成器表达式
3.生成器函数定义: 含有yield语句的函数是生成器函数,此函数被调用将返回一个生成器对象
4.生成器特点: 节省内存, 惰性机制(不到最后不拿值), 只前进不后退
2.生成器函数语法:
yield 表达式 # yield 翻译为(产生或生成)
yield 用于def函数中,目的是将此函数作为生成器函数使用
yield 用来生成数据,供next(it)函数使用


- # 写一个生成器函数my_integer(n)生成 1 到 n的整数
- def my_integer(n):
- i = 1 # 先初始化变量i将其设置为起始数
- while i < n: # 循环判断是否已到终止点,如果未到则生成
- yield i # 生成整数
- i += 1 # 控制循环条件
- for x in my_integer(5):
- print(x) # 1 2 3 4
示例1:


- # 生成器自动实现了迭代器协议,就是可迭代对象,本身就有内置的__next__()方法
- def test(): # 生成器的第一种表现方式,生成器函数的yield返回值
- print("第一次")
- yield 1
- print("第二次")
- yield 2 # yield可以多次返回
- g = test()
- print(g)
- print(g.__next__())
- print(g.__next__()) # 第二次执行从第一次的返回结果后开始执行
示例2:


- # 生成器实现fibonacci数列
- def create_num(all_num):
- a, b = 0, 1
- current_num = 0
- while current_num < all_num:
- yield a # 如果一个函数中有yield语句,那么这个就不在是函数而是一个生成器模板
- a, b = b, a+b
- current_num += 1
- return "最终返回结果"
- # 如果再调用函数的时候,发现这个函数中有yield,那么此时不是在调用函数,而是创建一个生成器对象
- obj1 = create_num(10)
- obj2 = create_num(5)
- for num in obj1:
- print("obj1", num)
- while True:
- try:
- ret = next(obj2)
- print("obj2:", ret)
- except Exception as ret:
- ret = ret.value # 最后return的结果只能在捕获异常里用value拿
- print(ret)
- break
示例3:


- # 生成器的send传参
- def create_num(all_num):
- a, b = 0, 1
- current_num = 0
- while current_num < all_num:
- ret = yield a
- print(">>>ret:", ret) # >>>ret: coco
- a, b = b, a+b
- current_num += 1
- obj1 = create_num(10)
- # obj.send(None) # send一般不会放到第一次启动生成器,如果非要使用,参数传递None
- ret = next(obj1)
- print(ret) #
- # send(x)作用同next(obj)一样让生成器向下执行一次
- # send(x)的参数会传递给上次返回位置,当做yield a的结果,然后ret保存这个结果
- # send(x)的结果同next(obj)一样是下一次调用yield时yield后面的值
- ret = obj1.send("coco") # 第一次调用send带参数会出错,因为没有接收参数的对象
- print(ret) #
示例4:


- # 生成器实现zip迭代工具函数内部原理
- def myzip(iter1, iter2):
- it1 = iter(iter1)
- it2 = iter(iter2)
- try:
- while True:
- a = next(it1)
- b = next(it2)
- yield (a, b)
- except:
- pass
- numbers = [10086, 10000, 10010, 95588]
- name = ["中国移动", "中国电信", "中国联通"]
- for i in myzip(numbers, name):
- print(i)
示例5:
3.生成器表达式语法:
gen = (表达式 for 变量 in 可迭代对象 [if 真值表达式]) # 用推导式形式生成一个新的生成器,[]内部的if部分可以省略


- gen = (x**2 for x in range(1, 5)) # gen绑定生成器对象,此对象为可迭代对象
- it = iter(gen) # 返回一个迭代器给it绑定
- next(it) #
- next(it) #
- next(it) #
- next(it) #
- next(it) # StopIteration
示例1:


- egg_list3 = ("数字%s" % i for i in range(10)) # 生成器的第二种表现方式: 生成器表达式
- print(egg_list3)
- print(egg_list3.__next__())
- print(egg_list3.__next__())
- print(next(egg_list3))
- print(sum(i for i in range(10000)))
示例2:


- # 寻找名字中带有两个e的⼈人的名字
- names = ([['Tom', 'Billy', 'Jefferson', 'Andrew', 'Steven', 'Joe'],
- ['Alice', 'Jill', 'Ana', 'Sherry', 'Eva']])
- # 不用推导式和表达式
- result = []
- for first in names:
- for name in first:
- if name.count("e") >= 2:
- result.append(name)
- print(result)
- # 推导式
- gen = (name for first in names for name in first if name.count("e") >= 2)
- for name in gen:
- print(name)
示例3:


- # 生成器惰性机制,不到最后不拿值
- def add(a, b):
- return a + b
- def test():
- for r_i in range(4):
- yield r_i
- g = test() # g是生成器
- for n in [2, 10]:
- g = (add(n, i) for i in g)
- # 过程分析
- # n=2-->g = (add(n, i) for i in g)
- # n=10-->g = (add(n, i) for i in (add(n, i) for i in g))
- # list(g)-->(add(10, i) for i in (add(10, i) for i in test())
- # list(g)-->(add(10, i) for i in (add(10, i) for i in [0, 1, 2, 3])
- # list(g)-->((add(10, i) for i in [10, 11, 12, 13])
- # 到达此步骤才开始取值
- print(list(g)) # [20, 21, 22, 23]
示例4:
21.生产者消费者模型
概述:
1.生产者是一个函数,消费者是另一个函数,内存缓冲区是一个有限的容器,处理函数间的的协作调度便是生产者消费者模型
2.内存缓冲区为空的时候消费者必须等待,而内存缓冲区满的时候,生产者必须等待,其他时候可以是动态平衡
3.可以运用生成器的特性实现协程调度


- # 常规的执行流程,先全部生产再全部消耗
- import time
- def producer():
- ret = []
- for i in range(100):
- time.sleep(0.03)
- ret.append("包子%s" % i)
- return ret
- def consumer(res):
- for index, bun in enumerate(res):
- time.sleep(0.1)
- print("第%s个人吃了%s" %(index, bun))
- res = producer()
- consumer(res)
- # 用生成器实现并发,一边生产一边消耗缓解内存压力
- import time
- def consumer(name):
- print("%s开始准备吃包子" % name)
- while True:
- bun = yield # send方法的参数赋值给yield,yield赋值给bun
- time.sleep(1)
- print("%s开始吃%s" % (name, bun))
- def producer():
- c1 = consumer("coco")
- c2 = consumer("dodo")
- print("")
- c1.__next__()
- c2.__next__()
- print("")
- for i in range(100):
- time.sleep(0.1)
- c1.send("包子%s" % i) # send方法不传参就等于__next__方法,传递参数让yield接收
- c2.send("包子%s" % i)
- producer()
示例:
22.名片管理系统
1.项目目录结构:
~/Desktop/Python/01_名片管理系统 $ tree
.
├── __pycache__
│ └── cards_tools.cpython-37.pyc
├── cards_main.py
└── cards_tools.py
2.cards_main.py文件代码


- #! /usr/bin/python3
- import cards_tools
- # 无限循环,由用户主动决定什么时候退出循环!
- while True:
- # 显示功能菜单
- cards_tools.show_menu()
- action_str = input("请选择希望执行的操作: ")
- print("您选择的操作是【%s】" % action_str)
- # 1,2,3 针对名片的操作
- if action_str in ["", "", ""]:
- # 新增名片
- if action_str == "":
- cards_tools.new_card()
- # 显示全部
- elif action_str == "":
- cards_tools.show_all()
- # 查询名片
- elif action_str == "":
- cards_tools.search_card()
- # 0 退出系统
- elif action_str == "":
- print("欢迎再次使用【名片管理系统】")
- break
- # 如果在开发程序时,不希望立刻编写分支内部的代码
- # 可以使用 pass 关键字,表示一个占位符,能够保证程序的代码结构正确!
- # 程序运行时,pass 关键字不会执行任何的操作!
- # pass
- # 其他内容输入错误,需要提示用户
- else:
- print("您输入的不正确,请重新选择")
cards_main.py
3.cards_tools.py文件代码


- # 记录所有的名片字典
- card_list = []
- def show_menu():
- """显示菜单"""
- print("*" * 50)
- print("欢迎使用【名片管理系统】V 1.0")
- print("")
- print("1. 新增名片")
- print("2. 显示全部")
- print("3. 搜索名片")
- print("")
- print("0. 退出系统")
- print("*" * 50)
- def new_card():
- """新增名片"""
- print("-" * 50)
- print("新增名片")
- # 1. 提示用户输入名片的详细信息
- name_str = input("请输入姓名: ")
- phone_str = input("请输入电话: ")
- qq_str = input("请输入QQ: ")
- email_str = input("请输入邮箱: ")
- # 2. 使用用户输入的信息建立一个名片字典
- card_dict = {"name": name_str,
- "phone": phone_str,
- "qq": qq_str,
- "email": email_str}
- # 3. 将名片字典添加到列表中
- card_list.append(card_dict)
- print(card_list)
- # 4. 提示用户添加成功
- print("添加 %s 的名片成功!" % name_str)
- def show_all():
- """显示所有名片"""
- print("-" * 50)
- print("显示所有名片")
- # 判断是否存在名片记录,如果没有,提示用户并且返回
- if len(card_list) == 0:
- print("当前没有任何的名片记录,请使用新增功能添加名片!")
- # return 可以返回一个函数的执行结果
- # 下方的代码不会被执行
- # 如果 return 后面没有任何的内容,表示会返回到调用函数的位置
- # 并且不返回任何的结果
- return
- # 打印表头
- for name in ["姓名", "电话", "QQ", "邮箱"]:
- print(name, end="\t\t")
- print("")
- # 打印分隔线
- print("=" * 50)
- # 遍历名片列表依次输出字典信息
- for card_dict in card_list:
- print("%s\t\t%s\t\t%s\t\t%s" % (card_dict["name"],
- card_dict["phone"],
- card_dict["qq"],
- card_dict["email"]))
- def search_card():
- """搜索名片"""
- print("-" * 50)
- print("搜索名片")
- # 1. 提示用户输入要搜索的姓名
- find_name = input("请输入要搜索的姓名: ")
- # 2. 遍历名片列表,查询要搜索的姓名,如果没有找到,需要提示用户
- for card_dict in card_list:
- if card_dict["name"] == find_name:
- print("姓名\t\t电话\t\tQQ\t\t邮箱")
- print("=" * 50)
- print("%s\t\t%s\t\t%s\t\t%s" % (card_dict["name"],
- card_dict["phone"],
- card_dict["qq"],
- card_dict["email"]))
- # 针对找到的名片记录执行修改和删除的操作
- deal_card(card_dict)
- break
- else:
- print("抱歉,没有找到 %s" % find_name)
- def deal_card(find_dict):
- """处理查找到的名片
- :param find_dict: 查找到的名片
- """
- print(find_dict)
- action_str = input("请选择要执行的操作 "
- "[1] 修改 [2] 删除 [0] 返回上级菜单")
- if action_str == "":
- find_dict["name"] = input_card_info(find_dict["name"], "姓名: ")
- find_dict["phone"] = input_card_info(find_dict["phone"], "电话: ")
- find_dict["qq"] = input_card_info(find_dict["qq"], "QQ: ")
- find_dict["email"] = input_card_info(find_dict["email"], "邮箱: ")
- print("修改名片成功!")
- elif action_str == "":
- card_list.remove(find_dict)
- print("删除名片成功!")
- def input_card_info(dict_value, tip_message):
- """输入名片信息
- :param dict_value: 字典中原有的值
- :param tip_message: 输入的提示文字
- :return: 如果用户输入了内容,就返回内容,否则返回字典中原有的值
- """
- # 1. 提示用户输入内容
- result_str = input(tip_message)
- # 2. 针对用户的输入进行判断,如果用户输入了内容,直接返回结果
- if len(result_str) > 0:
- return result_str
- # 3. 如果用户没有输入内容,返回 `字典中原有的值`
- else:
- return dict_value
cards_tools.py
4.完整项目网盘链接: https://pan.baidu.com/s/1QKW3Dhr5RxDrqRo6eXPC6w 密码: 59s2
23.信息管理系统
1.项目目录结构:
~/Desktop/Python/02_信息管理系统 $ tree
.
├── haproxy.conf
└── main.py
2.main.py文件代码


- import os
- def file_handler(backend_data, res=None, type="fetch"):
- """文件操作"""
- if type == "fetch": # 查询文件逻辑操作
- with open("haproxy.conf", "r") as read_f:
- tag = False
- ret = []
- for read_line in read_f:
- if read_line.strip() == backend_data:
- tag = True
- continue
- if tag and read_line.startswith("backend"):
- break
- if tag:
- print("\033[1;45m%s\033[0m" % read_line, end="")
- ret.append(read_line)
- return ret
- elif type == "change":
- # 读写文件
- with open("haproxy.conf", "r") as read_f, \
- open("haproxy.conf_new", "w") as write_f:
- tag = False # 头部状态警报
- has_write = False # 身体状态警报
- for read_line in read_f:
- # 判断读到的头部数据是否是我要找的头部数据
- if read_line.strip() == backend_data: # backend www.oldboy1.org
- tag = True
- continue
- # 头部数据找到情况下读到了下一个头部信息则重置头部警报
- if tag and read_line.startswith("backend"):
- tag = False
- # 没有找到头部情况下写其他读到的数据
- if not tag:
- write_f.write(read_line)
- else:
- if not has_write: # 找到头部后写表头和修改身体数据
- for record in res:
- write_f.write(record)
- has_write = True # 把身体列表写完后开起身体警报
- os.rename("haproxy.conf", "haproxy.conf.bak") # 备份源文件
- os.rename("haproxy.conf_new", "haproxy.conf") # 覆盖源文件
- os.remove("haproxy.conf.bak") # 删除备份后的源文件
- def fetch(data):
- """查询功能,返回查询结果ret"""
- print("\033[1;43m这是查询功能\033[0m") # \033[显示方式;前景色;背景色m
- print("\033[1;43m用户数据是\033[0m", data) # \033[0m 采用终端默认设置,即取消颜色设置
- backend_data = "backend %s" % data
- return file_handler(backend_data) # 返回调用文件操作函数执行结果
- def add(): # 添加
- pass
- def change(data):
- """修改传入的参数-->列表嵌套字典,列表的第0个元素是查找的数据,第1个元素是找到后要修改的数据
- [{'backend':'www.oldboy1.org','record':{'server':'2.2.2.5','weight':30,'maxconn':4000}},\
- {'backend':'www.oldboy1.org','record':{'server':'2.2.2.100','weight':20,'maxconn':3000}}]
- """
- backend = data[0]["backend"] # 取传入头部值 www.oldboy1.org','record
- backend_data = "backend %s" % backend # 拼接完整头部 backend www.oldboy1.org
- # 取传入身体--->要修改的值 'record':{'server':'2.2.2.5','weight':30,'maxconn':4000}
- oid_server_record = "%sserver %s %s weight %s maxconn %s\n" % (" " * 8, data[0]["record"]["server"],
- data[0]["record"]["server"],
- data[0]["record"]["weight"],
- data[0]["record"]["maxconn"])
- # 取传入身体--->修改后的值 'record':{'server':'2.2.2.100','weight':20,'maxconn':3000}
- new_server_record = "%sserver %s %s weight %s maxconn %s\n" % (" " * 8, data[1]["record"]["server"],
- data[1]["record"]["server"],
- data[1]["record"]["weight"],
- data[1]["record"]["maxconn"])
- # 测试输出拼接结果: server 2.2.2.5 2.2.2.5 weight 30 maxconn 4000
- print("要查找的数据是:%s" % oid_server_record)
- res = fetch(backend) # 调用查找函数fetch查找对应头部的身体数据并返回身体数据
- print(res)
- # 头部信息没有匹配到或者要修改的身体数据不在在返回的身体数据列表中
- if not res or oid_server_record not in res:
- return "修改的记录不存在"
- else:
- index = res.index(oid_server_record) # 取索引
- res[index] = new_server_record # 根据索引修改返回的身体数据
- res.insert(0, "%s\n" % backend_data) # 拼接完整的头部和身体: backend www.oldboy1.org 身体数据
- file_handler(backend_data, res=res, type="change") # 调用修改文件内容逻辑操作
- def delete(): # 删除
- pass
- if __name__ == "__main__":
- msg = """
- 1:查询
- 2:添加
- 3:修改
- 4:删除
- 5:退出
- """
- msg_dic = {
- "": fetch,
- "": add,
- "": change,
- "": delete
- }
- while True:
- print(msg) # 打印提示信息
- choice = input("请输入选项:").strip()
- if not choice: continue # 用户输入为空,跳过
- if choice == "": break # 退出程序
- data = input("请输入要查询的数据:").strip()
- if data != "":
- data = eval(data)
- res = msg_dic[choice](data) # 执行功能函数
- print(res) # 根据功能打印执行结果
- # 测试修改功能传递的修改参数
- # [{'backend': 'www.oldboy1.org', 'record': {'server': '2.2.2.5', 'weight': 30, 'maxconn': 4000}},
- # {'backend': 'www.oldboy1.org', 'record': {'server': '2.2.2.100', 'weight': 20, 'maxconn': 3000}}]
main.py
3.haproxy.conf文件代码


- global
- log 127.0.0.1 local2
- daemon
- maxconn 256
- log 127.0.0.1 local2 info
- defaults
- log global
- mode http
- timeout connect 5000ms
- timeout client 50000ms
- timeout server 50000ms
- option dontlognull
- listen stats :8888
- stats enable
- stats uri /admin
- stats auth admin:1234
- frontend oldboy.org
- bind 0.0.0.0:80
- option httplog
- option httpclose
- option forwardfor
- log global
- acl www hdr_reg(host) -i www.oldboy.org
- use_backend www.oldboy.org if www
- backend www.oldboy1.org
- server 101.1000.7.9 101.1000.7.9 weight 20 maxconn 30
- server 2.2.2.7 2.2.2.7 weight 30 maxconn 4000
- server 10.10.10.1 10.10.10.1 weight 22 maxconn 2000
- server 2.2.2.5 2.2.2.5 weight 30 maxconn 4000
- backend www.oldboy2.org
- server 3.3.3.3 3.3.3.3 weight 20 maxconn 3000
- backend www.oldboy20.org
- server 10.10.0.10 10.10.0.10 weight 9999 maxconn 33333333333
haproxy.conf
4.完整项目网盘链接: https://pan.baidu.com/s/1kZ7UovzbgCSzGgQ7D3e6Dw 密码: kppf
10_Python的函数function的更多相关文章
- Javascript自执行匿名函数(function() { })()的原理分析
匿名函数指没有指定函数名或指针的函数,自执行匿名函数只是其中一种,下文中称这种函数为:自执行函数 下面是一个最常见的自执行函数: // 传统匿名函数 (function() { alert('hell ...
- JavaScript自运行函数(function(){})()的理解
今天打开JQuery源文件(jquery-1.8.3), 看到JQuery的初始化过程是这样的 (function( window, undefined ) { // .... })( window ...
- 深入理解javascript中的立即执行函数(function(){…})()
投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...
- javaScript的函数(Function)对象的声明(@包括函数声明和函数表达式)
写作缘由: 平时再用js写函数的时候,一般都是以惯例 function fn () {} 的方式来声明一个函数,在阅读一些优秀插件的时候又不免见到 var fn = function () {} 这种 ...
- 函数(Function)作用域 / 远程函数执行
函数跟变量一样也是有作用域的:Global.Script.Local.Private Global:作用于整个PowerShell会话,只要PowerShell会话不结束,被Global修饰的变量和函 ...
- Javascript自执行匿名函数(function() { })()的原理浅析
匿名函数就是没有函数名的函数.这篇文章主要介绍了Javascript自执行匿名函数(function() { })()的原理浅析的相关资料,需要的朋友可以参考下 函数是JavaScript中最灵活的一 ...
- Javascript学习之函数(function)
在JS中,Function(函数)类型实际上是对象;每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法.由于函数是对象,因此函数名实际上也是一个指向函数对象的指针. 一 函 ...
- js立即执行函数: (function ( ){...})( ) 与 (function ( ){...}( ))
( function(){…} )() ( function (){…} () ) 是两种javascript立即执行函数的常见写法,最初我以为是一个括号包裹匿名函数,再在后面加个括号调用函数,最后达 ...
- 深入理解立即执行函数(function(){})();
( function(){-} )()和( function (){-} () )是两种javascript立即执行函数的常见写法,要理解立即执行函数,需要先理解一些函数的基本概念. 1,函数声明,函 ...
随机推荐
- 老板让我从上千个Excel中筛选数据,利用Python分分钟解决!
大家好,又到了Python办公自动化系列. 今天分享一个真实的办公自动化需求,大家一定要仔细阅读需求说明,在理解需求之后即可体会Python的强大! 很多人学习python,不知道从何学起.很多人学习 ...
- Vue watch 深层监听
Vue中监听某个对象的属性 为了避免监听整个对象导致效率问题,可以监听某个对象的特定属性 watch: { 'deptModel.depts': { handler(newVal, oldVal) { ...
- hdfs学习(二)
一.HDFS文件限额配置 在多人共用HDFS的环境下,配置设置非常重要.特别是在Hadoop处理大量资料的环境,如果没有配额管理,很容易把所有的空间用完造成别人无法存取.Hdfs的配额设定是针对目录而 ...
- 校内测试:T1秋末的落叶(命题人gxl)官方题解
秋末的落叶 题解 传送门:https://www.luogu.com.cn/problem/U121886 Part 1:疏通题意 首先,我们从题意和样例解释中很容易提取到以下信息: \(1.\)本题 ...
- Linux系统中有趣的命令(可以玩小游戏)
Linux系统中有趣的命令(可以玩小游戏) 前言 最近,我在看一些关于Linux系统的内容,这里面的内容是真的越学越枯燥,果然学习的过程还是不容易的.记得前几个月初学Linux时,有时候就会碰到小彩蛋 ...
- linux,运维,部署 相关
基础 linux基础命令 linux基础 部署 docker
- Dubbo直连方式
目录 一.dubbo概述 1. 基本架构 2. dubbo 支持的协议 二.直连方法 三.创建服务提供者 1. 思路 1. 创建maven web 2. pom.xml 3. 创建实体 4. 创建服务 ...
- python自动化测试中的数据驱动unittest+ddt
ddt是一个unittest的插件,用来实现uniitest的数据驱动 本文以python自动化测试中的数据驱动为原则,记录学习ddt的过程 一.数据的传递规则
- WS以及NW小世界网络的生成(MATLAB)
WS小世界网络生成算法,一般小世界网络生成算法速度慢,节点度分布与数学推导不符,在网络仿真中造成不便,这里针对实际网络动力学仿真过程撰写了WS小世界网络的MATLAB生成算法,并考虑了矩阵化,具有较高 ...
- make编译出错 usr/bin/ld: /data/app/openssl/lib/libcrypto.a(ecs_asn1.o): relocation R_X86_64_PC32 against symbol `ECDSA_SIG_it' can not be used when making a shared object; recompile with -fPIC
当make编译出现错误 usr/bin/ld: /data/app/openssl/lib/libcrypto.a(ecs_asn1.o): relocation R_X86_64_PC32 agai ...