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]
View Code

  max(salaries,key=func) #默认按key排序,key指定按什么来排序

    lambda 版本: max(salaries,key = lambda k:salaries[k])
View Code

  min()

    min(salaries,key = lambda k:salaries[k])
View Code

  sorted()

    sorted(salaries,key = lambda k:salaries[k],reverse = True)
View Code

  map(func,iterables) 不修改原数据

    l = ['alex','wupeiqi','alex']
    g = map(lambda x:x+'sb',l)
    for i in g:
    print(i)
View Code

  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)
View Code

偏函数

偏函数是从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)    
View Code

 

  递归两个过程:
    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)
View Code

模块与包

  一个模块就是一个包含了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__文件中利用相对路径进行导入子包的模块或者方法,来达到通过父包.方法来调用,屏蔽了不同子包路径过长的问题

posted @ 2017-06-03 10:07  SpeicalLife  阅读(297)  评论(0编辑  收藏  举报