Python自动化运维 - day5 - 函数part3、模块与包
协成函数
yield 1,叫做 yield 语句形式
x = yield , 叫做 yield 表达式形式 --> 协成函数
def eater(name): print('%s ready to eat' % name) while True: food = yield print('%s start to eat %s ' %(name,food)) g = eater('Alex') #产生生成器 g.send('Apple') #会触发函数的执行,并传递至给yield。
注意:
1)表达式形式的生成器,必须先使用next(),或者send(None),才可以让生成器执行。(生成器初始化)
2)后续可以使用next,但是无法传值,使用send 就可以传值
编写装饰器为 yield 表达式形式进行初始化
def gen(func): def wapper(*args,**kwargs): g = func(*args,**kwargs) g.send(None) return g return wapper @gen def eater(name): print('%s ready to eat ' % name ) while True: food = yield #这里的yield可以设置返回值 print('%s want to eat %s' %(name,food)) g = eater('daxin') g.send('Apple') 解读:
解读:
1)g.send('111'),先把111传给 yield ,由 yield 赋值给food,然后再往下执行,直到再次遇到yield后,停止
2)可以在 yield 后面设置返回值(比如,添加菜单,然后返回)
应用:grep -rl 'python' /root
import os def init(func): def wapper(*args,**kwargs): g = func(*args,**kwargs) next(g) return g return wapper @init def search(target): dirpath = yield src_file_path = os.walk(dirpath) while True: for roots,_,files in src_file_path: for file in files: filepath = "%s/%s" % (roots,file) target.send(filepath) @init def opener(target): while True: file_abs_path = yield with open(file_abs_path,encoding='utf-8') as filename: target.send((filename,file_abs_path)) @init def cat(target): while True: filename,file_abs_path = yield for line in filename: tag = target.send((line,file_abs_path)) if tag: break @init def grep(key,target): tag = False while True: line,file_abs_path = yield tag if key in line: tag = True target.send(file_abs_path) else: tag = False @init def printer(): while True: file_abs_path = yield print(file_abs_path) if __name__ == '__main__': dirpath = r'E:\StartPython\day7' keyname = 'socket' g = search(opener(cat(grep(keyname,printer())))) print(g) g.send(dirpath)
面向过程的程序设计,是一种流水线式的编程思路,是机械式的
优点:
1)程序结构清晰
缺点:
2)扩展性差
应用场景:
1)Linux就是一种面向过程编写的(C不支持面向对象)
2)git
3)httpd
匿名函数
没有名字的函数。适合比较简单的使用场景,函数体必须是返回值
lambda x,y: x+y
使用场景:
1)不需要名字的地方
内置函数(补充)
salaries = {'egon':3000,'alex':10000000,'wupeiqi':100000,'yuanhao':2000}
zip(iter1,iter2) 拉链函数,每次分别从iter1,iter2 取一个元素,组成元素,最后返回一个迭代器
def func(k): return salaries[k]
max(salaries,key=func) #默认按key排序,key指定按什么来排序
lambda 版本: max(salaries,key = lambda k:salaries[k])
min()
min(salaries,key = lambda k:salaries[k])
sorted()
sorted(salaries,key = lambda k:salaries[k],reverse = True)
map(func,iterables) 不修改原数据
l = ['alex','wupeiqi','alex'] g = map(lambda x:x+'sb',l) for i in g: print(i)
reduce(func,iterable,初始值)
from functools import reduce l = [1,2,3,4,5] reduce(lambda x,y:x+y,l)
1、由于没有指定初始值,所以先从list中pop一个值,然后迭代iterable
2、如果指定了初始值,就不会pop,直接把初始值作为第一个参数进行传递,然后迭代iterable
filter(func,iterable) 如果表达式的值为True,返回元素
l = ['Alex_sb','wupeiqi_sb','egon'] filter(lambad x: x.endswith('sb',l)
偏函数
偏函数是从Python2.5引入的一个概念,通过functools模块被用户调用。
偏函数是将所要承载的函数作为partial()
函数的第一个参数,原函数的各个参数依次作为partial()
函数后续的参数,除非使用关键字参数。看下面例子
def mod(n,m): return n%m mod_by_100 = functools.partial(mod,m=2) # 利用偏函数设置m的默认值为2 print(mod_by_100(11,m=5)) # 这里可以不传递m的值,默认就是2,如果要传递m的值,那么必须用关键字传递才可以(否则会被认为是为m赋值了两次)
int函数我们都知道是可以把一个字符串转换为int对象,它包含一个base参数,用于指出使用什么进制进行转换。
print(int('12345',base=16)) # 告诉int使用16进制对字符串进行转换
默认情况下base的值是10,如果要该为进制,那么就可以使用如下方式
def int2(str): return int(str,base=2) print(int2('00001111'))
如果使用偏函数的方式为:
int8 = functools.partial(int,base=8) print(int8('12345'))
PS:可以把 functools.partial 理解为帮助我们创建偏函数的,作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
需要注意的是如果没有使用关键字固定参数,那么它会默认的被添加在参数的最左边
max2 = functools.partial(max,10) print(max2(1,2,3,4)) # 这里实际上是 max(10,1,2,3,4)
递归调用
在函数调用过程中,直接或者间接调用了函数本身
查看递归循环支持的最大层数
import sys
查看:sys.getrecursionlimit()
设置:sys.setrecursionlimit(100000)
例子:
def age(n): if n == 1: return 18 return age(n-1) + 2 age(5)
递归两个过程:
1)递推
2)回溯
注意:
1、一定要有一个明确的结束条件
2、当进入更深一层递归时,问题规模相比上次递归都应该有所减少
3、递归效率不高,递归层次多会导致溢出(在计算机中,函数调用是通过栈,
应用:
二分法例子,给一个数组,找到就find out,否则就 no exist
注意:二分法的前提是,目标序列必须是有序的
a = [1,10,40,45,67,88,156,556,1234] def check(guest_list,guest_num): middle_num = len(guest_list)//2 if len(guest_list) == 1: print('not exist') else: if guest_num == guest_list[middle_num]: print('find it') elif guest_num > guest_list[middle_num]: check(guest_list[middle_num:],guest_num) else: check(guest_list[:middle_num],guest_num) check(a,9)
模块与包
一个模块就是一个包含了Python定义和声明的文件,文件名就是模块名加上.py的后缀
- 导入一个py文件,解释器解释该py文件
- 导入一个包,解释器解释该包下的 __init__.py 文件
如何使用模块
import
导入操作:
1)import导入模块就会执行要导入的模块的所有代码
2)产生新的名称空间
3)拿到一个模块名,指向模块中的代码
语法:
import module as alias 导入时并定义别名 import module,module1,module2 一次导入多个模块(不建议)
from
导入操作:
1)会执行要导入的模块的所有代码,调用时只通过func就可以调用
2)如果func和本文件中存在同名func,那么就会覆盖
语法:
from module import func 调用时可以直接使用func,而不必使用module.func来调用 from module import * 导入所有模块
扩展:
在被导入模块中定义__all__ = ['module','module1'],这样在导入其时,用*,只能导入 __all__ 定义的模块
注意:
1) func来自哪个文件,那么func内部调用的变量,以其所在文件中的环境变量为准
模块中的特殊关键字:
__file__ :打印当前文件的文件名
__name__ :显示当前模块是否被导入
显示:__main__ 表示没有被导入
显示:模块名, 表示被次模块被导入
扩展用法:
if __name__ == '__main__' 这样写,在别的地方导入,就不会执行下面的代码段,而直接运行文件时,就会执行
模块路径:
导入模块的搜索顺序:
1)先从内存中寻找
2)然后在内置模块中寻找
3)然后在sys.path中查找
sys.path:
1)类似于shell中的PATH
2)list类型,每个元素为路径的字符串形式
3)列表的第一个路径默认为当前路径
加入sys.path
sys.path.append('dirpath')
包
从目录级别来组织模块的,本质上也是被导入的
目录下有 __init__ 文件的都可以理解为包,在import时,其实导入的就是包下的 __init__.py 文件。
1、无论是import形式,还是from...import形似,凡是在导入语句中(而不是使用时)遇到带点的,就是在导入包
2、包是目录级的(文件夹),文件夹是用来组织py文件的。(包的本质就是一个包含 __init__.py 文件的目录)
3、import导入文件时,产生的名称空间来源于文件,import包,产生的命名空间同样来源于文件,既包下面的__init__.py,导入包的本质就是导入该文件
例子:
多个级联的包的__init__的例子,代码自己写
通过包直接调用子包模块下的函数,方便使用者
1、直接导入__init__的例子
2、包内部直接导入运行,尽量避免使用import,要使用from module import func
3、在包内部引用自己包内的其他模块的路径
1)绝对导入,直接从顶层包开始写(如果修改报名,就很麻烦)
2)相对导入,以当前路径为记住利用.来表示,.表示当前目录,..表示上一级目录,...表示上上一级路面,既.表示一级目录
包的import *
这个* 其实读取的就是 __init__ 文件里面的所有函数,所以一般不要用*
在 __init__ 中定义 __all__=['module1','module2'],会把 __all__ 当前目录下的module,module,返回,那么就可以直接使用module,来调用了
最佳实践
通过在父包内的__init__文件中利用相对路径进行导入子包的模块或者方法,来达到通过父包.方法来调用,屏蔽了不同子包路径过长的问题