zoukankan      html  css  js  c++  java
  • PYTHON_DAY_05

    目录

    一.匿名函数 (lambda())

    二.内置函数 (map()、filter()、reduce()、zip()、sorted())

    三.yield关键字的另外一种用法

    四.递归调用

    五.正则表达式 re模块

    六.模块与包


    一.匿名函数(lambda)

    1.当传入函数时,有些时候不需要显示的定义函数,直接传入匿名函数更方便

    2.匿名函数提供了有限支持,以map()为例,计算f(x)=x²时,除了定义一个f(x),还可以直接传入匿名函数:

    >>> map(lambda x: x*x ,[1,2,3,4,5,6,7,8,9])
    [1,4,9,16,25,36,49,64,81]
    

    匿名函数lambda x: x*x 实际上就是:(冒号前面的x 表示函数参数)

     def f(x):

       return x * x

    3.匿名函数只能有一个表达式,不用写return,返回值就是表达式的结果

    此外,匿名函数也是一个函数对象,也可以把它赋值给一个变量,再利用变量调用该函数:

    >>> f = lambda x: x * x
    >>> f
    <function <lambda> at 0x10453d7d0>
    >>> f(5)
    25
    

    同样,也可以把它作为返回值返回:

    def build(x, y):
        return lambda: x * x + y * y
    

     

    二.内置函数

    max([1,5,2,9])                   # 求最大值

    min([9,2,-4,2])                  # 求最小值

    zip()函数             # 压缩任意多个[0,n]参数 ,形成新的复合元组

    a, b = [0, 1], [2, 3]
    ab = zip(a, b)
    print(list(ab))
    # [(0, 2), (1, 3)]

     

    sorted()函数        # 返回正序的序列

    salaries={
    'egon':3000,
    'alex':100000000,
    'wupeiqi':10000,
    'yuanhao':2000
    }
    
    print(sorted(salaries))     
    #默认的排序结果是从小到大,这里默认k值的首字母
    
    print(sorted(salaries,key=lambda x:salaries[x]))     
    #默认的排序结果是从小到大,这里按照薪资排序
    
    print(sorted(salaries,key=lambda x:salaries[x],reverse=True))     
    #默认的排序结果是从小到大,参数是反转内置函数reverse
    

        

    map()函数

    map()是Python的内置函数。它的第一个参数是一个函数对象。

    re = map((lambda x: x+3),[1,3,5,6])

    这里,map()有两个参数,一个是lambda所定义的函数对象,一个是包含有多个元素的表。map()的功能是将函数对象依次作用于表的每一个元素,每次作用的结果储存于返回的表re中。map通过读入的函数(这里是lambda函数)来操作数据(这里“数据”是表中的每一个元素,“操作”是对每个数据加3)。

    在Python 3.X中,map()的返回值是一个循环对象。可以利用list()函数,将该循环对象转换成表。

    如果作为参数的函数对象有多个参数,可使用下面的方式,向map()传递函数参数的多个参数:

    re = map((lambda x,y: x+y),[1,2,3],[6,7,9])

    map()将每次从两个表中分别取出一个元素,带入lambda所定义的函数。

    filter()函数

    filter函数的第一个参数也是一个函数对象。它也是将作为参数的函数对象作用于多个元素。如果函数对象返回的是True,则该次的元素被储存于返回的表中。filter通过读入的函数来筛选数据。同样,在Python 3.X中,filter返回的不是表,而是循环对象。

    filter函数的使用如下例:

    def func(a):
        if a > 100:
            return True
        else:
            return False
    
    print filter(func,[10,56,101,500])
    

      

    l=['alex_SB','wupeiqi_SB','yuanhao_SB','egon']
    
    res=filter(lambda x:x.endswith('SB'),l)
    print(res)
    print(list(res))
    

      

    reduce()函数

    reduce函数的第一个参数也是函数,但有一个要求,就是这个函数自身能接收两个参数。reduce可以累进地将函数作用于各个参数。如下例:

    print reduce((lambda x,y: x+y),[1,2,5,7,9])

    reduce的第一个参数是lambda函数,它接收两个参数x,y, 返回x+y。

    reduce将表中的前两个元素(1和2)传递给lambda函数,得到3。该返回值(3)将作为lambda函数的第一个参数,而表中的下一个元素(5)作为lambda函数的第二个参数,进行下一次的对lambda函数的调用,得到8。依次调用lambda函数,每次lambda函数的第一个参数是上一次运算结果,而第二个参数为表中的下一个元素,直到表中没有剩余元素。

    上面例子,相当于(((1+2)+5)+7)+9

    from functools import reduce
    
    L=[1,2,3,4,5]
    print(reduce(lambda x,y:x+y,L,0))
    

      

    三.yield关键字的另外一种用法

    yield的语句形式:yield 1

    yield的表达是形式:x = yield

    模拟餐厅吃饭,首先我们需要点餐,然后服务员把一道菜一个个端上来:

    def deco(func):                                         # 装饰器
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            next(res)                                           # 初始化的作用返回一个food
            return res
        return wrapper
    
    @deco
    def eater(name):
        print('%s ready to eat' %name)
        food_list=[]                                             # 菜单功能
        while True:
            food=yield food_list                                   # yield控制返回值
            food_list.append(food)
            print('%s start to eat %s' %(name,food))
    
    g=eater('alex')             
    g.send('脚趾头')                                                # send等同于next(g)触发函数运行(初始化) 但send同时又传值的功能    函数内部有yield就是一个生成器,生成器必须先初始化  

    总结x = yield表达式:

    g.send('1111'),先把1111传给yield,由yield赋值给x,然后再往下执行,直到再次碰到yield,然后把yield后的返回值返回

    yield表达式应用实例:  # grep -rl 'python' /root                     ( 执行过滤'python'的操作,查找root目录下所有子目录的文件包含python)


    执行下面的方法的思维:


    第一步:把它定义成函数的形式,找到所有文件夹下的文件

    def search(searth_path):            # 参数为路径
        g=os.walk(searth_path)
        for par_dir,_,files in g:       #返回的三个参数分别为:主目录、子目录、文件
            for file in files:
                file_abs_path=r'%s\%s' %(par_dir,file)      # 拼接成所要的格式
                # print(file_abs_path)
    
    search(r'E:资料、笔记python_oldboys17day05day5a')
    

    第二部:要想找文件需要先建立一个打开函数
    第三部:遍历打开文件每一行
    第四部:过滤每一行 用户想要的内容
    第五步:打印想要的结果
    最后一步:调用执行

    import os
    
    def init(func):                     # 装饰器
        def wrapper(*args,**kwargs):
            res=func(*args,**kwargs)
            next(res)
            return res
        return wrapper
    
    @init
    def search(target):             # target是下面打开函数生成器的名称
        while True:
            search_path=yield
            g=os.walk(search_path)
            for par_dir,_,files in g:
                for file in files:
                    file_abs_path=r'%s\%s' %(par_dir,file)
                    # print(file_abs_path)
                    target.send(file_abs_path)
    
    @init
    def opener(target):
        while True:
            file_abs_path=yield
            # print('opener func==>',file_abs_path)
            with open(file_abs_path,encoding='utf-8') as f:
                target.send((file_abs_path,f))
    
    @init
    def cat(target):                                    # 遍历打开文件的每一行的函数
        while True:
            file_abs_path,f=yield  #(file_abs_path,f)       # yield传上一个函数给的值 绝对路径和文件名
            for line in f:
                tag=target.send((file_abs_path,line))
                if tag:
                    break
    
    @init
    def grep(target,pattern):                           # 过滤函数 参数是用户想要的过滤内容
        tag=False
        while True:
            file_abs_path,line=yield tag            # yield传上一个函数给的值
            tag=False
            if pattern in line:
                tag=True
                target.send(file_abs_path)
    
    @init
    def printer():                                  # 打印函数 打印想要的结果
        while True:
            file_abs_path=yield
            print(file_abs_path)
    
    
    
    x=r'C:UsersAdministratorPycharmProjectspython17期day5a' # 这是想要找的路径 想在这里找到过滤内容
    
    g=search(opener(cat(grep(printer(),'python'))))
    print(g)                                                     # 现在拿到的是生成器对象
    
    g.send(x)                                                   # 要想执行结果 需要send()
    
    
    ## 具体文件见17期_day05的代码笔记
    

     

    四.递归调用

     

    优点:是逻辑简单清晰

    缺点:是过深的调用会导致栈溢出。

    Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题。

    尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

    例子1:计算5的阶乘

    #!/usr/bin/env python
     
    def func(n):
      if n == 0:
        return 1
      else:
        return n * func(n-1)
     
    print func(5)
    

         

    我们计算fact(5),可以根据函数定义看到计算过程如下:
    
    ===> fact(5)
    ===> 5 * fact(4)
    ===> 5 * (4 * fact(3))
    ===> 5 * (4 * (3 * fact(2)))
    ===> 5 * (4 * (3 * (2 * fact(1))))
    ===> 5 * (4 * (3 * (2 * 1)))
    ===> 5 * (4 * (3 * 2))
    ===> 5 * (4 * 6)
    ===> 5 * 24
    ===> 120
    

      

      

    例子2:计算1到100的和

     
    #!/usr/bin/env python
     
    def func(n):
      sum = 0
      if n == 0:
        return 0
      else:
        return n + func(n-1)
     
    print func(100)
    

      

    例子3:用递归的方法打印目录下的所有文件名

    #!/usr/bin/env python
     
    import os
    import sys
     
    def listdir(n):
      lsdir = os.listdir(n)
      for i in lsdir:
        if os.path.isfile(os.path.join(n,i)):
          print os.path.join(n,i)
        else:
          listdir(os.path.join(n,i))
          listdir(sys.argv[1])
    

      

    改进版:
    #!/usr/bin/env python
     
    import os
    import sys
     
    def print_file(path):
      lsdir = os.listdir(path)
      files = [ i for i in lsdir if os.path.isfile(os.path.join(path,i)) ]
      dicts = [ i for i in lsdir if os.path.isdir(os.path.join(path,i)) ]
      if files:
        for i in files:
          print os.path.join(path,i)
      if dicts:
        for f in dicts:
          print_file(os.path.join(path,f))
          print_file(sys.argv[1])
    

      

    用到的模块

      os.pass.isdir()
        - 判断后面的文件是否是目录,是的话返回True(不能判断目录是否存在,不存在也会返回false)
      os.path.isfile()
        - 判断后面的文件是否是文件,是的话返回True(不能判断文件是否存在,不存在也会返回false)
      os.path.join()
        - 连接括号中的路径(文件)
        os.path.join('/etc/','passwd','abc') ----> /etc/passwd/abc
     

    注意

      Python3默认递归的深度不能超过100层



    五.正则表达式 re模块

    正则表达式(regular expression)主要功能是从字符串(string)中通过特定的模式(pattern),搜索想要找到的内容。

    语法


    之前,我们简介了字符串相关的处理函数。我们可以通过这些函数实现简单的搜索功能,比如说从字符串“I love you”中搜索是否有“you”这一子字符串。但有些时候,我们只是模糊地知道我们想要找什么,而不能具体说出我是在找“you”,比如说,我想找出字符串中包含的数字,这些数字可以是0到9中的任何一个。这些模糊的目标可以作为信息写入正则表达式,传递给Python,从而让Python知道我们想要找的是什么。

    在Python中使用正则表达式需要标准库中的一个包re。

    import re
    m = re.search('[0-9]','abcd4ef')
    print(m.group(0))

    re.search()接收两个参数,第一个'[0-9]'就是我们所说的正则表达式,它告诉Python的是,“听着,我从字符串想要找的是从0到9的一个数字字符”。

    re.search()如果从第二个参数找到符合要求的子字符串,就返回一个对象m,你可以通过m.group()的方法查看搜索到的结果。如果没有找到符合要求的字符,re.search()会返回None。

    如果你熟悉Linux或者Perl, 你应该已经熟悉正则表达式。当我们打开Linux shell的时候,可以用正则表达式去查找或着删除我们想要的文件,比如说:

    $rm book[0-9][0-9].txt

    这就是要删除类似于book02.txt的文件。book[0-9][0-9].txt所包含的信息是,以book开头,后面跟两个数字字符,之后跟有".txt"的文件名。如果不符合条件的文件名,比如说:

    bo12.txt

    book1.txt

    book99.text

    都不会被选中。

    Perl中内建有正则表达式的功能,据说是所有正则表达式系统中最强的,这也是Perl成为系统管理员利器的一个原因。

    正则表达式的函数


    m = re.search(pattern, string)  # 搜索整个字符串,直到发现符合的子字符串。
    m = re.match(pattern, string)   # 从头开始检查字符串是否符合正则表达式。必须从字符串的第一个字符开始就相符。

    可以从这两个函数中选择一个进行搜索。上面的例子中,我们如果使用re.match()的话,则会得到None,因为字符串的起始为‘a’, 不符合'[0-9]'的要求。

    对于返回的m, 我们使用m.group()来调用结果。(我们会在后面更详细解释m.group())

    我们还可以在搜索之后将搜索到的子字符串进行替换:

    str = re.sub(pattern, replacement, string) 
    # 在string中利用正则变换pattern进行搜索,对于搜索到的字符串,用另一字符串replacement替换。返回替换后的字符串。

    此外,常用的正则表达式函数还有

    re.split()    # 根据正则表达式分割字符串, 将分割后的所有子字符串放在一个表(list)中返回

    re.findall()  # 根据正则表达式搜索字符串,将所有符合的子字符串放在一给表(list)中返回

    (在熟悉了上面的函数后,可以看一下re.compile(),以便于提高搜索效率。)

    写一个正则表达式


    关键在于将信息写成一个正则表达式。我们先看正则表达式的常用语法:

    1)单个字符:

    .          任意的一个字符

    a|b        字符a或字符b

    [afg]      a或者f或者g的一个字符        

    [0-4]      0-4范围内的一个字符

    [a-f]      a-f范围内的一个字符

    [^m]       不是m的一个字符

    s         一个空格

    S         一个非空格

    d         [0-9]

    D         [^0-9]

    w         [0-9a-zA-Z]

    W         [^0-9a-zA-Z]

    2)重复

    紧跟在单个字符之后,表示多个这样类似的字符

    *         重复 >=0 次

    +         重复 >=1 次

    ?         重复 0或者1 次

    {m}       重复m次。比如说 a{4}相当于aaaa,再比如说[1-3]{2}相当于[1-3][1-3]

    {m, n}    重复m到n次。比如说a{2, 5}表示a重复2到5次。小于m次的重复,或者大于n次的重复都不符合条件。

    正则表达          相符的字符串举例

    [0-9]{3,5}       9678

    a?b                b

    a+b              aaaaab

    3) 位置

    ^         字符串的起始位置

    $         字符串的结尾位置

    正则表达          相符的字符串举例        不相符字符串

    ^ab.*c$          abeec               cabeec (如果用re.search(), 将无法找到。)


    4)返回控制

    我们有可能对搜索的结果进行进一步精简信息。比如下面一个正则表达式:

    output_(d{4})

    该正则表达式用括号()包围了一个小的正则表达式,d{4}。 这个小的正则表达式被用于从结果中筛选想要的信息(在这里是四位数字)。这样被括号圈起来的正则表达式的一部分,称为群(group)。
    我们可以m.group(number)的方法来查询群。group(0)是整个正则表达的搜索结果,group(1)是第一个群……

    import re
    m = re.search("output_(d{4})", "output_1986.txt")
    print(m.group(1))

    我们还可以将群命名,以便更好地使用m.group查询:

    import re
    m = re.search("output_(?P<year>d{4})", "output_1986.txt")   #(?P<name>...) 为group命名
    print(m.group("year"))

      

     练习题:

    # 练习
    # 有一个文件,文件名为output_1981.10.21.txt 。
    # 下面使用Python: 读取文件名中的日期时间信息,并找出这一天是周几。
    # 将文件改名为output_YYYY-MM-DD-W.txt (YYYY:四位的年,MM:两位的月份,DD:两位的日,W:一位的周几,并假设周一为一周第一天)
    
    import re
    import time
    import datetime
    
    filename = "output_1981.10.21.txt"
    m = re.search("output_(d{4}.d{2}.d{2})", filename)
    searchResult = m.group(1)
    print("matcht result: %s" % searchResult)
    dates = searchResult.split('.')
    for date in dates:
        print(date)
    year = dates[0]
    month = dates[1]
    day = dates[2]
    xingqi = datetime.datetime(int(year), int(month), int(day)).strftime("%w")
    # replace to new filename
    theReplacePart = '%s-%s-%s-%s' % (year, month, day, xingqi)
    print('the new filename is: %s' % theReplacePart)
    newfileName = re.sub("d{4}.d{2}.d{2}", theReplacePart, filename)
    print(newfileName)
    

      

    六.模块与包  

    我们之前看到了函数和对象。从本质上来说,它们都是为了更好的组织已经有的程序,以方便重复利用。

    模块(module)也是为了同样的目的。在Python中,一个.py文件就构成一个模块。通过模块,你可以调用其它文件中的程序。

    引入模块


    我们先写一个first.py文件,内容如下:

    def laugh():
        print 'HaHaHaHa'

    再写一个second.py,并引入first中的程序:

    import first
    
    for i in range(10):
        first.laugh()

    在second.py中,我们使用了first.py中定义的laugh()函数。

    引入模块后,可以通过模块.对象的方式来调用引入模块中的某个对象。上面例子中,first为引入的模块,laugh()是我们所引入的对象。

    Python中还有其它的引入方式:


    import a as b             # 引入模块a,并将模块a重命名为b

    from a import function1   # 从模块a中引入function1对象。调用a中对象时,我们不用再说明模块,即直接使用function1,而不是a.function1。

    from a import *           # 从模块a中引入所有对象。调用a中对象时,我们不用再说明模块,即直接使用对象,而不是a.对象。

    也可以在模块a里写,__all__=['调用对象名']  ==  from a import *

    这些引用方式,可以方便后面的程序书写。

    __name__ == '__main__'的作用


    有句话经典的概括了这段代码的意义:

    “Make a script both importable and executable”

    意思就是说让你写的脚本模块既可以导入到别的模块中用,另外该模块自己也可执行

    这句话,可能一开始听的还不是很懂。下面举例说明:

    先写一个模块:

    #module.py
    
    def main():
      print "we are in %s"%__name__
    if __name__ == '__main__':
      main()
    

      

    这个函数定义了一个main函数,我们执行一下该py文件发现结果是打印出”we are in __main__“,说明我们的if语句中的内容被执行了,调用了main():

    但是如果我们从另我一个模块导入该模块,并调用一次main()函数会是怎样的结果呢?

    #anothermodle.py
    
    from module import main
    main()
    
    其执行的结果是:we are in module

    但是没有显示”we are in __main__“,也就是说模块__name__ = '__main__' 下面的函数没有执行。

    这样既可以让“模块”文件运行,也可以被其他模块引入,而且不会执行函数2次。这才是关键。

    总结一下:


    如果我们是直接执行某个.py文件的时候,该文件中那么”__name__ == '__main__'“是True,但是我们如果从另外一个.py文件通过import导入该文件的时候,这时__name__的值就是我们这个py文件的名字而不是__main__。

    这个功能还有一个用处:调试代码的时候,在”if __name__ == '__main__'“中加入一些我们的调试代码,我们可以让外部模块调用的时候不执行我们的调试代码,但是如果我们想排查问题的时候,直接执行该模块文件,调试代码能够正常运行!

    搜索路径


    Python会在以下路径中搜索模块顺序:

    1. 内存   只在第一次导入执行后开辟内存空间  多次导入不生效    
    2. 内置
    3. sys.path

    如果你有自定义的模块,或者下载的模块,可以根据情况放在相应的路径,以便Python可以找到。

    包(package)


    可以将功能相似的模块放在同一个文件夹(比如说this_dir)中,构成一个模块包。通过

    import this_dir.module

    引入this_dir文件夹中的module模块。


     注 意 : .点左边必须是一个包 .点的右边一定是文件名


    通常包总是一个目录,可以使用import导入包,或者from + import来导入包中的部分模块。包目录下为首的一个文件便是 __init__.py。然后是一些模块文件和子目录,假如子目录中也有 __init__.py 那么它就是这个包的子包了。

    在创建许许多多模块后,我们可能希望将某些功能相近的文件组织在同一文件夹下,这里就需要运用包的概念了。包对应于文件夹,使用包的方式跟模块也类似,唯一需要注意的是,当文件夹当作包使用时,文件夹需要包含__init__.py文件,主要是为了避免将文件夹名当作普通的字符串。__init__.py的内容可以为空,一般用来进行包的某些初始化工作或者设置__all__值,__all__是在from package-name import *这语句使用的,全部导出定义过的模块。

    可以从包中导入单独的模块。

    1). import PackageA.SubPackageA.ModuleA,使用时必须用全路径名

    2). 变种: from PackageA.SubPackageA import ModuleA, 可以直接使用模块名而不用加上包前缀。

    3). 也可以直接导入模块中的函数或变量:from PackageA.SubPackageA.ModuleA import functionA

    import语句语法:

    1. 当使用from package import item时,item可以是package的子模块或子包,或是其他的定义在包中的名字(比如一个函数、类或变量)

       首先检查item是否定义在包中,不过没找到,就认为item是一个模块并尝试加载它,失败时会抛出一个ImportError异常。

    2. 当使用import item.subitem.subsubitem语法时,最后一个item之前的item必须是包,最后一个item可以是一个模块或包,但不能是类、函数和变量

    3. from pacakge import *

       如果包的__init__.py定义了一个名为__all__的列表变量,它包含的模块名字的列表将作为被导入的模块列表。

       如果没有定义__all__, 这条语句不会导入所有的package的子模块,它只保证包package被导入,然后导入定义在包中的所有名字。

    python包是:
    包是一个有层次的文件目录结构,它定义了由n个模块或n个子包组成的python应用程序执行环境。
    通俗一点:包是一个包含__init__.py 文件的目录,该目录下一定得有这个__init__.py文件和其它模块或子包。

     

     

    常见问题:

    • 引入某一特定路径下的模块

      • 使用sys.path.append(yourmodulepath)
    • 将一个路径加入到python系统路径下,避免每次通过代码指定路径

      • 利用系统环境变量 export PYTHONPATH=$PYTHONPATH:yourmodulepath
      • 直接将这个路径链接到类似/Library/Python/2.7/site-packages目录下
    • 好的建议

      • 经常使用if __name__ == '__main__',保证你写包既可以import又可以独立运行,用于test。

      • 多次import不会多次执行模块,只会执行一次。可以使用reload来强制运行模块,但不提倡。

      


     为了组织好模块,将多个模块分为一个包。包是python模块文件所在的目录,且该目录下必须存在__init__.py文件。

    常见的包结构如下:

    package_a
    ├── __init__.py
    ├── module_a1.py
    └── module_a2.py
    package_b
    ├── __init__.py
    ├── module_b1.py
    └── module_b2.py
    main.py
    
    • 如果 main.py 想要引用 packagea 中的模块 modulea1,可以使用:
        from package_a import module_a1 import package_a.module_a1
    • 如果 packagea 中的 modulea1需要引用 packageb,那么默认情况下,python是找不到 packageb。我们可以使用 sys.path.append('../'),可以在packagea中的__init__.py添加这句话,然后该包下得所有module都添加* import __init_即可。
    
    
  • 相关阅读:
    20199124 2019-2020-2《网络攻防实践》第8周作业
    20199124 2019-2020-2《网络攻防实践》第7周作业
    20199124 2019-2020-2《网络攻防实践》第6周作业
    20199124 2019-2020-2 《网络攻防实践》-第5周作业
    20199124 2019-2020-2 《网络攻防实践》第4周作业
    20199124 2019-2020-2 《网络攻防实践》第3周作业
    20199124 2019-2020-2 《网络攻防实践》第2周作业
    20199124 2019-2020-2 《网络攻防实践》第1周作业
    《网络攻防实践》假期作业
    《构建之法》第一章学习总结
  • 原文地址:https://www.cnblogs.com/lipingzong/p/6924284.html
Copyright © 2011-2022 走看看