美文网首页python web
第5天,协程函数、匿名函数、模块与包、正则

第5天,协程函数、匿名函数、模块与包、正则

作者: CaiGuangyin | 来源:发表于2017-06-03 11:50 被阅读31次

    目录:

    一、协程函数
        表达式形式的yield的用途
    二、函数的递归调用
    三、匿名函数lambda
        max()
        min()
        sorted()
        map()
        reduce()
        filter()
    四、模块
        1.什么是模块
        2. 为何要使用模块
        3. 导入模块
            3.1 import
            3.2 from … import …
            3.3 把模块当做脚本执行
            3.4 模块搜索路径
            3.5 dir()函数
    五、包
        5.1 import
        5.2 from … import …
        5.3 __init__.py文件
        5.4 from glance.api import *
        5.5 绝对导入和相对导入
        5.6 单独导入包
    六、re模块
        re模块提供的方法
    

    上一节课生成器还有一些知识点没讲到,接下来补充;

    一、协程函数

    生成器:yield关键字的另外一种用法
    yield的语句形式有两种:

    • yield n //n为yield的返回值
    • x = yield n //yield的表达式形式,n为yield返回值

    现在模拟去餐厅吃饭,服务员上一盘菜,你就吃一盘,然后等着服务员再继续上菜

    def eater(name):
        print('%s ready eat something ...'%name)
        food_list = []
        while True:
            food = yield food_list
            print('%s eat a %s'%(name,food))
            food_list.append(food)
    
    g=eater('alex')    # g是一个生成器对象
    next(g)            # 第一次必须给yield传一个空值
    res = g.send('饺子')      #g.send('饺子')就相当于服务员上菜
    print(res)
    g.send('包子')
    res1 = g.send('饼子')
    print(res1)
    

    输出结果:

    alex ready eat something ...
    alex eat a 饺子
    ['饺子']
    alex eat a 包子
    alex eat a 饼子
    ['饺子', '包子', '饼子']
    

    执行流程解析:

    1. g=eater('alex') ,g是一个生成器对象,这一步并不会使eater()函数执行;
    2. 遇到next(g),会触发eater('alex')开始执行;
    3. eater('alex')执行中遇到yield关键字时,函数执行暂停在此处,并返回yield后面的值food_list(此时food_list是一个空列表),跳出到函数外面触发eater('alex')执行的一行(next(g)所在的那一行);
    4. next(g)接收yield返回的food_list(此时是空列表),并继续往下执行;
    5. 当遇到g.send('饺子')时,会将'饺子’传给yield,并触发eater('alex')从上次暂停的位置(yield处)开始执行,yield接收g.send('饺子')传进来的值,并赋值给food (此时food = '饺子'),然后eater('alex')函数继续往下执行;
    6. while True死循环,循环到下一次yield时,暂停执行,将food_list返回给g.send('饺子'),此时的food_list加入了food(food='饺子'),并将food_list赋值给res;
    7. 继续往下执行,每当遇到g.send()就会重复第5步的流程,遇到yield就会重复第6步的流程;

    总结:

    • next(g)g.send('x')都能触发生成器函数的执行,不同的是send可以给yiled传值;
    • 第一次给yield传值,必须传空值,next(g)也相当于给yield传了一个空值,所以next(g)也可用g.send(None)替换;
    • 由于给每个生成器函数传值之前必须先传一个空值,这样很容易让人忘掉此步骤,基于此,可以将此步骤写成一个装饰器去实现;这样就可以在多处使用了;请看下面的实现代码 。

    应用如下:

    def init(func):
        '''这个装饰器的作用是初始化其他生成器函数''' 
        def wrapper(*args,**kwargs):
            g=func(*args,**kwargs)
            next(g)
            return g
        return wrapper
    
    init            # eater = init(eater)
    def eater(name):
        print('%s ready eat something ...'%name)
        food_list = []
        while True:
            food = yield food_list
            print('%s eat a %s'%(name,food))
            food_list.append(food)
    
    g=eater('alex')
    # res=next(g)
    g.send('饺子')
    g.send('包子')
    g.send('饼子')
    print(g.send('牛奶'))
    

    输出结果:

    alex ready eat something ...
    alex eat a 饺子
    alex eat a 包子
    alex eat a 饼子
    alex eat a 牛奶
    ['饺子', '包子', '饼子', '牛奶']
    

    可以看出,注释掉res = next(g)后,执行结果与之前无异。

    表达式形式的yield的用途

    # 实现类似grep -rl ‘xxx’ /root 的功能;
    # 实现的功能是查找出root目录下所有包含xxx的文件,并将文件名返回;
    
    import os
    
    def init(func):
        '''这个装饰器的作用:初始化一个生成器'''
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            next(res)
            return res
        return wrapper
    
    init
    def ergodic(target):
        '''提取传入的目录下所有文件的绝对路径,发送给opener()'''
        while True:
            path = yield
            file_list = os.walk(path)
            for par_dir,_,files in file_list:
                for file in files:
                    file_abs_path = r'%s\%s'%(par_dir,file)
                    target.send(file_abs_path)
    
    init
    def opener(target):
        '''接收ergodic()传递的文件绝对路径,打开,并读取每行一内容,传递给grep()'''
        while True:
            file_abs_path = yield
            with open(file_abs_path,encoding='utf-8') as f:
                for line in f:
                    tag = target.send((file_abs_path,line))
                    if tag:
                        break
    
    @init
    def grep(target,pattern):
        '''
        接收opener()传递的每一行,判断pattern(需要搜索的关键字)是否在行中,\
        然后将文件绝对路径传给printer()
        '''
        tag = False
        while True:
            file_abs_path,line = yield tag
            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)
    
    gen = ergodic(opener(grep(printer(),'python')))
    path = r'E:\python\s17\day05\练习\a'
    gen.send(path)
    

    执行结果:

    E:\python\s17\day05\练习\a\a.txt
    E:\python\s17\day05\练习\a\b\b.txt
    E:\python\s17\day05\练习\a\b\c\d\d.txt
    E:\python\s17\day05\练习\a\b1\b.txt
    

    二、函数的递归调用

    在函数调用过程中,直接或间接地调用了函数本身, 这就是函数的递归调用,如下示例:

    def f1():
        print('from f1')
        f1()
    
    f1()
    

    注:上述代码,f1()的函数体内又调用了它本身,当在外部调用f1()时,会产生一个死循环(python默认递归调用设定为1000次,可以通过下述方法获取);

    import sys
    print(sys.getrecursionlimit())   #获取python递归调用限制次数
    sys.setrecursionlimit(100000)    #设定递归调用限制次数
    print(sys.getrecursionlimit())
    
    # 输出结果:
    1000
    100000
    

    递归的特性:

    1. 必须有一个明确的结束条件;
    2. 每次进入更深一层递归时,问题规模相比上次递归都应该有所减少;
    3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层帧。由于栈的大小不是无限的,所以递归调用的次数过多,会导致栈溢出)
      堆栈扫盲,请点击查看

    递归函数实际应用,二分查找:

    l = [1, 2, 10,33,53,71,73,75,77,85,101,201,202,999,11111]
    
    def search(find_num,seq):
        if len(seq) == 0:
            print('not exists')
            return
        mid_index=len(seq)//2
        mid_num=seq[mid_index]
        print(seq,mid_num)
        if find_num > mid_num:
            #in the right
            seq=seq[mid_index+1:]
            search(find_num,seq)
        elif find_num < mid_num:
            #in the left
            seq=seq[:mid_index]
            search(find_num,seq)
        else:
            print('found it')
    
    search(77,l)    
    search(72,l)    # 72不存在l数字列表中
    

    输出结果:

    [1, 2, 10, 33, 53, 71, 73, 75, 77, 85, 101, 201, 202, 999, 11111] 75
    [77, 85, 101, 201, 202, 999, 11111] 201
    [77, 85, 101] 85
    [77] 77
    您要查找的数字: 77
    found it
    [1, 2, 10, 33, 53, 71, 73, 75, 77, 85, 101, 201, 202, 999, 11111] 75
    [1, 2, 10, 33, 53, 71, 73] 33
    [53, 71, 73] 71
    [73] 73
    not exists
    

    三、匿名函数lambda

    匿名函数就是不需要显式的指定函数

    python 使用 lambda 来创建匿名函数。

    • lambda只是一个表达式,函数体比def简单很多。
    • lambda的主体是一个表达式,而不是一个代码块。仅仅能lambda表达式中封装有限的逻辑进去。
    • lambda函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
    • 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。

    语法
    lambda函数的语法只包含一个语句,如下:
    lambda [arg1 [,arg2,.....argn]]:expression
    如下实例:

    #!/usr/bin/python
    # -*- coding: UTF-8 -*-
     
    # 可写函数说明
    sum = lambda arg1, arg2: arg1 + arg2;
     
    # 调用sum函数
    print("相加后的值为 : ", sum( 10, 20 ))
    print("相加后的值为 : ", sum( 20, 20 ))
    
    # 以上实例输出结果:
    相加后的值为 :  30
    相加后的值为 :  40
    
    def calc(n):
        return n**n
    print(calc(3))    # 结果27
    
    # 换成匿名函数
    calc1=lambda x:x**x
    print(cal1c(3))   # 结果27
    

    lambda 会将:冒号后面的表达式结果返回

    这么用,感觉好像匿名函数也没有什么用。
    不过匿名函数主要是和其它函数搭配使用的。
    如下,将匿名函数和内置函数max()、min()、sorted()、zip()。。。结合起来使用

    max()

    max()函数的功能是获取序列最大值
    以下例子用max()函数获取工资最高的人名

    salaries={
        'egon':3000,
        'alex':100000000,
        'wupeiqi':10000,
        'yuanhao':2000
    }
    
    def func(k):
        return salaries[k]
    
    res = max(salaries,key=func)     #1
    print(res)
    
    # 输出结果:
    alex
    

    #1 max()函数会遍历字典salaries中的key,将key当作参数传给函数func() ,然后函数func()返回key对应的value然后赋值给max()括号中的key,并根据这个key进行比较,然后返回工资最高的人名

    将以上代码通过匿名函数简化:

    salaries={
        'egon':3000,
        'alex':100000000,
        'wupeiqi':10000,
        'yuanhao':2000
    }
    
    res = max(salaries,key=lambda k:salaries[k])
    print(res)
    
    # 输出结果:
    alex
    

    min()

    获取序列最小值,用法和max()函数相同

    salaries={
        'egon':3000,
        'alex':100000000,
        'wupeiqi':10000,
        'yuanhao':2000
    }
    
    res = min(salaries,key=lambda x:salaries[x])
    print(res)
    
    # 输出结果:
    yuanhao
    

    sorted()

    序列化数据排序,默认是从小到大排序,加reverse=True关键字可以从大到小排序,用法也和max()类似

    以下示例,输出工资从低到高的名字

    salaries={
        'egon':3000,
        'alex':100000000,
        'wupeiqi':10000,
        'yuanhao':2000
    }
    
    print(sorted(salaries,key=lambda x:salaries[x]))
    
    # 输出结果:
    ['yuanhao', 'egon', 'wupeiqi', 'alex']
    

    reverse=True反向排序(从大到小):

    salaries={
        'egon':3000,
        'alex':100000000,
        'wupeiqi':10000,
        'yuanhao':2000
    }
    
    print(sorted(salaries,key=lambda x:salaries[x],reverse=True))
    
    # 输出结果:
    ['alex', 'wupeiqi', 'egon', 'yuanhao']
    

    map()

    map()函数接收两个参数,一个是函数,一个是iterable(可迭代对象),map将传入的函数依次作用到序列的每个元素,并把结果作为新的iterable返回。

    以下示例 ,将不符合规范的名字变成符合规范的首字母大写的名字

    name_li = ['adam','LISA','barI']
    print(list(map(lambda x:x.capitalize(),name_li)))
    
    # 输出结果:
    ['Adam', 'Lisa', 'Bari']
    

    将每个名字末尾加上指定字符

    name_list = ['alex','wupeiqi','yuanhao']
    print(list(map(lambda x:x+'_god',name_list)))
    
    # 输出结果:
    ['alex_god', 'wupeiqi_god', 'yuanhao_god']
    

    reduce()

    python2中reduce()还是一个单独的内置函数;python3中则将reduce()集成到functools包中了,使用之前需要先导入from functools import reduce

    reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

    reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
    
    # f假设是一个函数
    

    如下示例,对一个序列求和

    from functools import reduce
    num_li = [1,2,3,4,5]
    res = reduce(lambda x,y:x+y,num_li)
    print(res)
    
    # 输出结果:
    15
    

    reduce一个初始值:

    from functools import reduce
    num_li = [1,2,3,4,5]
    res = reduce(lambda x,y:x+y,num_li,10)
    print(res)
    
    # 输出结果:
    25
    

    map()reduce()结合使用
    将字典中的value取出并组合

    def fn(x, y):
        return x * 10 + y
    
    def char2num(s):
        return {'1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    
    print(reduce(fn, map(char2num, '13579')))
    
    # 输出结果:
    13579
    

    filter()

    filter函数用于过滤序列
    map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

    过虑以‘god’结尾的名字

    name_li = ['alex_god', 'wupeiqi_god', 'yuanhao_god','egon']
    
    res = filter(lambda x:x.endswith('god'),name_li)
        # res 得到的是一个filter对象
        # <filter object at 0x000...748>
    print(list(res))
    
    # 输出结果:
    ['alex_god', 'wupeiqi_god', 'yuanhao_god']
    

    注意:
    map()filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫map()filter()完成计算结果,需要用list()函数获得所有结果并返回list。

    四、模块

    1.什么是模块

    一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀

    2. 为何要使用模块

    • 如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。

    • 随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用。

    3. 导入模块

    import ...from ... import ...两种

    3.1 import

    示例文件:smap.py, 文件名是spam.py , 模块名是spam

    #spam.py
    print('from the spam.py')
    
    money=1000
    
    def read1():
        print('spam->read1->money',money)
    
    def read2():
        print('spam->read2 calling read')
        read1()
    
    def change():
        global money
        money=0
    

    模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行,(import语句可以在程序中的任意位置使用,且针对同一个模块可以import多次,为了防止重复导入,python的优化手段是:第一次导入后就将模块名加载到内存中,后续的import语句仅是对已经加载到内存中的模块对象增加了一次引用,不会重新执行模块内的语句) 如下:

    #test.py
    import spam #只在第一次导入时才执行spam.py内代码,此处的显示效果是只打印一次'from the spam.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
    import spam
    import spam
    import spam
    
    '''
    执行结果:
    from the spam.py
    '''
    

    我们可以从sys.modules中找到当前已经加载的模块,sys.modules的结果是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入。

    print(sys.modules)
    
    # 输出结果
    {... ,'spam': <module 'spam' from 'E:\\python\\s17\\day05\\spam.py'>,...
    

    首次import导入spam模块干的事:

    1. 为源文件(spam模块)创建新的命名空间,要spam中定义的函数和方法若是使用到了global时,访问的就是这个名称空间;
    2. 在新创建的全称空间中执行模块中包含的代码;
    3. 创建名字spam来引用该命名空间,这个名字和变量名没什么区别,都是‘第一类的’,且使用spam.名字的方式可以访问spam.py文件中定义的名字,spam.名字与test.py中的名字来自两个完全不同的地方。

    为模块起别名

    import spam as sm
    

    为已经导入的模块起别名的方式对编写可扩展的代码很有用,假设有两个模块xmlreader.py和csvreader.py,它们都定义了函数read_data(filename):用来从文件中读取一些数据,但采用不同的输入格式。可以编写代码来选择性地挑选读取模块,例如:

    if file_format == 'xml':
        import xmlreader as reader
    elif file_format == 'csv':
        import csvreader as reader
    data=reader.read_date(filename)
    

    在一行导入多个模块

    import sys,os,re
    

    3.2 from ... import ...

    对比import spam,会将源文件的名称空间'spam'带到当前名称空间中,使用时必须是spam.名字的方式;

    而from 语句相当于import,也会创建新的名称空间,但是将spam中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了。

    from spam import read1,read2
    

    这样在当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间

    
    #测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
    #test.py
    from spam import read1
    money = 10
    read1()
    
    '''
    执行结果:
    from the spam.py
    spam->read1->money 1000
    '''
    
    #测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
    #test.py
    from spam import read2
    def read1():
        print('==========')
    read2()
    
    '''
    执行结果:
    from the spam.py
    spam->read2 calling read
    spam->read1->money 1000
    '''
    

    如果当前有重名的read1或read2,那么会覆盖导入的read1和read2

    也支持as起别名

    from spam import read1 as read
    

    也支持导入多行

    from spam import (read1,
                      read2,
                      money)
    

    *from spam import ***
    from spam import * 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为
    你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。

    可以使用__all__来控制 *
    在spam.py中新增一行

    __all__ = ['money','read1']
    # 这样在另外一个文件用from spam import * 就只能导入列表中存在的两个名字
    

    3.3 把模块当做脚本执行

    我们可以通过模块的全局变量__name__来查看模块名:
    当做脚本运行:

    __name__ 等于 '__main__'
    

    当做模块导入:

    __name__ = 文件名
    

    作用:用来控制 .py文件在不同的应用场景下执行不同的逻辑

    #fib.py
    
    def fib(n):    # write Fibonacci series up to n
        a, b = 0, 1
        while b < n:
            print(b, end=' ')
            a, b = b, a+b
        print()
    
    def fib2(n):   # return Fibonacci series up to n
        result = []
        a, b = 0, 1
        while b < n:
            result.append(b)
            a, b = b, a+b
        return result
    
    # 如果是当做脚本执行,则会执行下列代码,当作模块导入时,则不会执行下述代码
    if __name__ == "__main__":
        import sys
        fib(int(sys.argv[1]))
    

    3.4 模块搜索路径

    模块的查找顺序:

    • 1.内存中已经加载的模块
    • 2.内置模块
    • 3.sys.path路径中包含的模块

    需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名。

    在初始化后,python程序可以修改sys.path,路径放到前面的优先于标准库被加载。

    import sys
    sys.path.append('/a/b/c/d')
    sys.path.insert(0,'/x/y/z') #排在前的目录,优先被搜索
    

    注意:搜索时按照sys.path中从左到右的顺序查找,位于前的优先被查找,sys.path中还可能包含.zip归档文件和.egg文件,python会把.zip归档文件当成一个目录去处理。

    #windows下的路径不加r开头,会语法错误
    sys.path.insert(0,r'C:\Users\Administrator\PycharmProjects\a')
    

    至于.egg文件是由setuptools创建的包,这是按照第三方python库和扩展时使用的一种常见格式,.egg文件实际上只是添加了额外元数据(如版本号,依赖项等)的.zip文件。

    3.5 dir()函数

    内建函数dir是用来查找模块中定义的名字,返回一个有序字符串列表

    import spam
    dir(spam)
    

    如果没有参数,dir()列举出当前定义的名字

    dir()不会列举出内建函数或者变量的名字,它们都被定义到了标准模块builtin中,可以列举出它们,

    import builtins
    dir(builtins)
    

    五、包

    包是一种通过使用‘.模块名’来组织python模块名称空间的方式。

    1. 无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法;

    2. 包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含init.py文件的目录)

    3. import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的init.py,导入包本质就是在导入该文件

    注意事项:
    1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。
    2.对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
    3.对比import item 和from item import name的应用场景:
    如果我们想直接使用name那必须使用后者。

    glance/                   #Top-level package
    
    ├── __init__.py      #Initialize the glance package
    
    ├── api                  #Subpackage for api
    
    │   ├── __init__.py
    
    │   ├── policy.py
    
    │   └── versions.py
    
    ├── cmd                #Subpackage for cmd
    
    │   ├── __init__.py
    
    │   └── manage.py
    
    └── db                  #Subpackage for db
    
        ├── __init__.py
    
        └── models.py
    

    5.1 import

    我们在与包glance同级别的文件中测试

    import glance.db.models
    glance.db.models.register_models('mysql') 
    

    5.2 from ... import ...

    需要注意的是from后import导入的模块,必须是明确的一个不能带点,否则会有语法错误,如:from a import b.c是错误语法

    我们在与包glance同级别的文件中测试

    from glance.db import models
    models.register_models('mysql')
     
    from glance.db.models import register_models
    register_models('mysql')
    

    5.3 __init__.py文件

    不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的init.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码。

    5.4 from glance.api import *

    在讲模块时,我们已经讨论过了从一个模块内导入所有,此处我们研究从一个包导入所有

    此处是想从包api中导入所有,实际上该语句只会导入包api下init.py文件中定义的名字,我们可以在这个文件中定义all_:

    #在__init__.py中定义
    x=10
    
    def func():
        print('from api.__init.py')
    
    __all__=['x','func','policy']
    

    此时我们在于glance同级的文件中执行from glance.api import *就导入all中的内容(versions仍然不能导入)。

    5.5 绝对导入和相对导入

    我们的最顶级包glance是写给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:

    绝对导入:以glance作为起始

    相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)

    例如:我们在glance/api/version.py中想要导入glance/cmd/manage.py

    在glance/api/version.py
    
    #绝对导入
    from glance.cmd import manage
    manage.main()
    
    #相对导入
    from ..cmd import manage
    manage.main()
    

    测试结果:注意一定要在于glance同级的文件中测试

    from glance.api import versions
    

    注意:在使用pycharm时,有的情况会为你多做一些事情,这是软件相关的东西,会影响你对模块导入的理解,因而在测试时,一定要回到命令行去执行,模拟我们生产环境,你总不能拿着pycharm去上线代码吧!!!

    特别需要注意的是:可以用import导入内置或者第三方模块(已经在sys.path中),但是要绝对避免使用import来导入自定义包的子模块(没有在sys.path中),应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。

    5.6 单独导入包

    单独导入包名称时不会导入包中所有包含的所有子模块,如:

    #在与glance同级的test.py中
    import glance
    glance.cmd.manage.main()
    
    '''
    执行结果:
    AttributeError: module 'glance' has no attribute 'cmd'
    
    '''
    

    解决方法一:

    #在glance/__init__.py中写:
    import glance.cmd.manage
    

    解决方法二:

    1 #在glance/__init__.py中写:
    2 from . import cmd
    3 
    4 #在glance/cmd/__init__.py中写:
    5 from . import manage
    

    验证:

    1 #在于glance同级的test.py中执行
    2 import glance
    3 glance.cmd.manage.main()
    

    六、re模块

    正则表达式常用匹配模式(元字符)

    匹配模式

    re模块提供的方法

    import re
    #1
    print(re.findall('e','alex make love'))   #返回结果['e', 'e', 'e'],返回所有满足匹配条件的结果,放在列表里
    
    #2
    print(re.search('e','alex make love').group()) #返回结果e,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
    
    #3
    print(re.match('e','alex make love'))    #返回None,同search,不过在字符串开始处进行匹配,完全可以用search+^代替match
    
    #4
    print(re.split('[ab]','cadbcd')) #返回['c', 'd', 'cd'],先按'a'分割得到'c'和'dbcd',再对'c'和'dbcd'分别按'b'分割,得到['c', 'd', 'cd']
    
    #5
    print('===>',re.sub('a','A','alex make love')) #===> Alex mAke love,不指定n,默认替换所有
    print('===>',re.sub('a','A','alex make love',1)) #===> Alex make love
    print('===>',re.sub('a','A','alex make love',2)) #===> Alex mAke love #替换前两个匹配
    print('===>',re.sub('^(\w+)(.*?\s)(\w+)(.*?\s)(\w+)(.*?)$',r'\5\2\3\4\1','alex make love')) #===> love make alex  #反向引用
    
    #6
    print('===>',re.subn('a','A','alex make love')) #===> ('Alex mAke love', 2),结果带有总共替换的个数
    
    #6
    obj=re.compile('\d{2}')
    
    print(obj.search('abc123eeee').group()) #返回12
    print(obj.findall('abc123eeee')) #返回['12'],重用了obj
    
    # 补充一
    import re
    print(re.findall("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>")) #['h1']
    print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").group()) #<h1>hello</h1>
    print(re.search("<(?P<tag_name>\w+)>\w+</(?P=tag_name)>","<h1>hello</h1>").groupdict()) #<h1>hello</h1>
    
    print(re.search(r"<(\w+)>\w+</(\w+)>","<h1>hello</h1>").group())
    print(re.search(r"<(\w+)>\w+</\1>","<h1>hello</h1>").group())
    
    # 补充二
    import re
    
    print(re.findall(r'-?\d+\.?\d*',"1-12*(60+(-40.35/5)-(-4*3))")) #找出所有数字['1', '-12', '60', '-40.35', '5', '-4', '3']
    
    #使用|,先匹配的先生效,|左边是匹配小数,而findall最终结果是查看分组,所有即使匹配成功小数也不会存入结果
    #而不是小数时,就去匹配(-?\d+),匹配到的自然就是,非小数的数,在此处即整数
    
    print(re.findall(r"-?\d+\.\d*|(-?\d+)","1-2*(60+(-40.35/5)-(-4*3))")) #找出所有整数['1', '-2', '60', '', '5', '-4', '3']
    
    #_*_coding:utf-8_*_
    __author__ = 'Linhaifeng'
    #在线调试工具:tool.oschina.net/regex/#
    import re
    
    s='''
    http://www.baidu.com
    egon@oldboyedu.com
    你好
    010-3141
    '''
    
    #最常规匹配
    # content='Hello 123 456 World_This is a Regex Demo'
    # res=re.match('Hello\s\d\d\d\s\d{3}\s\w{10}.*Demo',content)
    # print(res)
    # print(res.group())
    # print(res.span())
    
    #泛匹配
    # content='Hello 123 456 World_This is a Regex Demo'
    # res=re.match('^Hello.*Demo',content)
    # print(res.group())
    
    
    #匹配目标,获得指定数据
    
    # content='Hello 123 456 World_This is a Regex Demo'
    # res=re.match('^Hello\s(\d+)\s(\d+)\s.*Demo',content)
    # print(res.group()) #取所有匹配的内容
    # print(res.group(1)) #取匹配的第一个括号内的内容
    # print(res.group(2)) #去陪陪的第二个括号内的内容
    
    
    
    #贪婪匹配:.*代表匹配尽可能多的字符
    # import re
    # content='Hello 123 456 World_This is a Regex Demo'
    #
    # res=re.match('^He.*(\d+).*Demo$',content)
    # print(res.group(1)) #只打印6,因为.*会尽可能多的匹配,然后后面跟至少一个数字
    
    
    #非贪婪匹配:?匹配尽可能少的字符
    # import re
    # content='Hello 123 456 World_This is a Regex Demo'
    #
    # res=re.match('^He.*?(\d+).*Demo$',content)
    # print(res.group(1)) #只打印6,因为.*会尽可能多的匹配,然后后面跟至少一个数字
    
    
    #匹配模式:.不能匹配换行符
    content='''Hello 123456 World_This
    is a Regex Demo
    '''
    # res=re.match('He.*?(\d+).*?Demo$',content)
    # print(res) #输出None
    
    # res=re.match('He.*?(\d+).*?Demo$',content,re.S) #re.S让.可以匹配换行符
    # print(res)
    # print(res.group(1))
    
    
    #转义:\
    
    # content='price is $5.00'
    # res=re.match('price is $5.00',content)
    # print(res)
    #
    # res=re.match('price is \$5\.00',content)
    # print(res)
    
    
    #总结:尽量精简,详细的如下
        # 尽量使用泛匹配模式.*
        # 尽量使用非贪婪模式:.*?
        # 使用括号得到匹配目标:用group(n)去取得结果
        # 有换行符就用re.S:修改模式
    
    
    
    #re.search:会扫描整个字符串,不会从头开始,找到第一个匹配的结果就会返回
    
    # import re
    # content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
    #
    # res=re.match('Hello.*?(\d+).*?Demo',content)
    # print(res) #输出结果为None
    
    #
    # import re
    # content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
    #
    # res=re.search('Hello.*?(\d+).*?Demo',content) #
    # print(res.group(1)) #输出结果为
    
    
    
    #re.search:只要一个结果,匹配演练,
    import re
    content='''
    <tbody>
    <tr id="4766303201494371851675" class="even "><td><div class="hd"><span class="num">1</span><div class="rk "><span class="u-icn u-icn-75"></span></div></div></td><td class="rank"><div class="f-cb"><div class="tt"><a href="/song?id=476630320">![](http://p1.music.126.net/Wl7T1LBRhZFg0O26nnR2iQ==/19217264230385030.jpg?param=50y50&quality=100)</a><span data-res-id="476630320" "
    # res=re.search('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
    # print(res.group(1))
    
    
    #re.findall:找到符合条件的所有结果
    # res=re.findall('<a\shref=.*?<b\stitle="(.*?)".*?b>',content)
    # for i in res:
    #     print(i)
    
    
    #re.sub:字符串替换
    import re
    content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
    
    # content=re.sub('\d+','',content)
    # print(content)
    
    
    #用\1取得第一个括号的内容
    #用法:将123与456换位置
    # import re
    # content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
    #
    # # content=re.sub('(Extra.*?)(\d+)(\s)(\d+)(.*?strings)',r'\1\4\3\2\5',content)
    # content=re.sub('(\d+)(\s)(\d+)',r'\3\2\1',content)
    # print(content)
    
    
    
    
    # import re
    # content='Extra strings Hello 123 456 World_This is a Regex Demo Extra strings'
    #
    # res=re.search('Extra.*?(\d+).*strings',content)
    # print(res.group(1))
    
    
    # import requests,re
    # respone=requests.get('https://book.douban.com/').text
    
    # print(respone)
    # print('======'*1000)
    # print('======'*1000)
    # print('======'*1000)
    # print('======'*1000)
    # res=re.findall('<li.*?cover.*?href="(.*?)".*?title="(.*?)">.*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span.*?</li>',respone,re.S)
    # # res=re.findall('<li.*?cover.*?href="(.*?)".*?more-meta.*?author">(.*?)</span.*?year">(.*?)</span.*?publisher">(.*?)</span>.*?</li>',respone,re.S)
    #
    #
    # for i in res:
    #     print('%s    %s    %s   %s' %(i[0].strip(),i[1].strip(),i[2].strip(),i[3].strip()))
    

    相关文章

      网友评论

        本文标题:第5天,协程函数、匿名函数、模块与包、正则

        本文链接:https://www.haomeiwen.com/subject/jhgpfxtx.html