5.python(迭代器,装饰器,生成器,基本算法,正则)  (2)对于原生支持随机访问的数据结构(如tuple、list),迭代器和经典for循环的索引访问相比并无优势,反而丢失了索引值(可以使用内建函数enumerate()找回这个索引值)。但对于无法随机访问的数据结构(比如set)而言,迭代器是唯一的访问元素的方式。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件,或是斐波那契数列等等。迭代器更大的功劳是提供了一个统一的访问集合的接口,只要定义了__iter__()方法对象,就可以使用迭代器访问。

一,迭代器

1.迭代器

 (1)迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,知道所有的元素被访问完结束。迭代器只能往前不会后退.

迭代器提供两种方法:

(1)__next__() 返回迭代器的下一个元素

1
2
3
4
5
6
7
8
9
10
11
12
names = iter(['liu','yao','sb'])
print(names)
print(names.__next__())
print(names.__next__())
print('暂停')
print(names.__next__())
输出:
<list_iterator object at 0x0000000000B2B6A0>
liu
yao
暂停
sb

(2)__iter__()返回迭代器对象本身

1
2
3
4
names = iter(['liu','yao','sb'])
print(names.__iter__())
输出:
<list_iterator object at 0x00000000006DB6D8>

2.生成器

从Python2.2起,生成器提供了一种简洁的方式帮助返回列表元素的函数来完成简单和有效的代码。

定义:一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator),如果函数中包含yield语法,那这个函数就会变成生成器 
它基于yield指令,允许停止函数并立即返回结果。
此函数保存其执行上下文,如果需要,可立即继续执行。..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def cash_money(amount):
    while amount >0:
        amount -= 100
        yield 100
        print('随便取')
  
atm = cash_money(500)
print(type(atm))
print(atm.__next__())
print('暂停')
print(atm.__next__())
输出
<class 'generator'>
100
暂停
随便取
100

作用:

这个yield的主要效果呢,就是可以使函数中断,并保存中断状态,中断后,代码可以继续往下执行,过一段时间还可以再重新调用这个函数,从上次yield的下一句开始执行。

3.yield异步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time
def consumer(name):
    print("%s准备吃包子啦" %(name))
    while True:
        baozi = yield
        print("包子来了[%s],被[%s]吃了!"%(baozi,name))
def producer(name):
    c1 = consumer('mayun')
    c2 = consumer('mahuateng')
    c1.__next__()
    c2.__next__()
    print('%s要开始做包子啦'%(name))
    for i in range(1,10):
        time.sleep(1)
        print('做了两个包子')
        c1.send(i)
        c2.send(i)
producer('yaoao')

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
mayun准备吃包子啦
mahuateng准备吃包子啦
yaoao要开始做包子啦
做了两个包子
包子来了[1],被[mayun]吃了!
包子来了[1],被[mahuateng]吃了!
做了两个包子
包子来了[2],被[mayun]吃了!
包子来了[2],被[mahuateng]吃了!
做了两个包子
包子来了[3],被[mayun]吃了!
包子来了[3],被[mahuateng]吃了!
做了两个包子
包子来了[4],被[mayun]吃了!
包子来了[4],被[mahuateng]吃了!
做了两个包子
包子来了[5],被[mayun]吃了!
包子来了[5],被[mahuateng]吃了!
做了两个包子
包子来了[6],被[mayun]吃了!
包子来了[6],被[mahuateng]吃了!
做了两个包子
包子来了[7],被[mayun]吃了!
包子来了[7],被[mahuateng]吃了!
做了两个包子
包子来了[8],被[mayun]吃了!
包子来了[8],被[mahuateng]吃了!
做了两个包子
包子来了[9],被[mayun]吃了!
包子来了[9],被[mahuateng]吃了!

二,装饰器

装饰器是函数,只不过该函数可以具有特殊的含义,装饰器用来装饰函数或类,使用装饰器可以在函数执行前和执行后添加相应操作。

装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

例:如果一个公司有运维部,开发部,测试部,设计部,等,并且公司具有基础平台架构,为公司各个部门提供数据库调用,资料查看,监控等。当这些部门想使用这些功能的时候,直接调用这些功能的接口就可以,如下:

1
2
3
4
5
6
7
8
9
######基础平台提供的功能------------
def 功能1()
    print ('功能1')
def 功能2()
    print ('功能2')
def 功能3()
    print ('功能3')
def 功能4()
    print ('功能4')

当运维部门调用的时候如下:

1
2
3
def 功能1()
def 功能2()
def 功能3()

当开发部门调用的时候如下:

1
2
3
def 功能1()
def 功能2()
def 功能3()

之后要为平台提供的所有功能添加验证机制,

基础平台提供如下功能接口:

1.让各个部门修改自己的代码

2.在每个部门实现的功能上加上代码

3.把验证代码变成函数 在每个功能上加入

4.为了追寻开放封闭原则

利用装饰器的功能实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def login(func):
    def inner():
        # 验证1
        # 验证2
        # 验证3
        return func()
    return inner
@login
def 功能1():
    print '功能1'
@login
def 功能2():
    print '功能2'
@login
def 功能3():
    print '功能3'
@login
def 功能4():
    print '功能4'

当各个部门执行def功能的时候

1
2
3
4
5
6
7
8
def login(func):
    def inner():
        # 验证1
        return func()
    return inner
@login
def 功能1():
    print '功能1'

当调用功能1的时候 会先把功能1的函数名带入内存地址,之后会执行login函数,func为功能1,之后inner会将功能1的参数带入等待执行inner的验证功能后,会将参数交给func执行功能1的命令。

三,递归

递归函数

递归算法所体现的“重复”一般有三个要求:
一是每次调用在规模上都有所缩小(通常是减半);
二是相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入);
三是在问题的规模极小时必须用直接给出解答而不再进行递归调用,因而每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束。

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

1
2
3
4
5
6
7
8
9
def calc(n):
    print(n)
    if n/2 > 1:
        res = calc(n/2)
        print('res',res)
    print('N',n)
    return n
 
calc(10)

四,二分法

二分法是能够更快更好的去取得值所在的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def binary_search(data_source,find_n):
    mid = int(len(data_source)/2)
    if len(data_source)>=1:
        if data_source[mid] > find_n:
            print('data in left of [ID 33[35;1m %s 33[0m]'%data_source[mid])
            binary_search(data_source[:mid],find_n)
        elif data_source[mid] < find_n:
            print('data in right of [ID 33[32;1m %s 33[0m]'%data_source[mid])
            binary_search(data_source[mid:],find_n)
        else:
            print('found find',data_source[mid])
    else:
        print('not found')
if __name__ == "__main__":
    data = list(range(1,10000))
    binary_search(data,1)

五,斐波那契数列

   斐波那契数列指的是这样的数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368

用python实现:

1
2
3
4
5
6
7
8
def func(arg1,arg2,stop):
    if arg1 == 0:
        print(arg1)
    arg3 = arg1 + arg2
    print(arg3)
    if arg3 < stop:
        func(arg2,arg3,stop)
func(0,1,(100000))

六、二维数组

生成一个4*4的2维数组并将其顺时针旋转90度

1
2
3
4
5
6
7
8
9
data = [[col for col in range(4)] for row in range(4)]
print('-----------------------')
for r_index,row in enumerate(data):
    for c_index in range(r_index,len(row)):
        tmp = data[c_index][r_index]
        data[c_index][r_index] = row[c_index]
        data[r_index][c_index] = tmp
    print('----------------------')
    for r in data:print(r)

七。冒泡

它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

例:将一个不规则的数组按从小到大的顺序进行排序

1
2
3
4
5
6
7
8
9
10
11
12
data = [10,4,33,21,54,3,8,11,5,22,2,1,17,13,6]
print("原始:",data)
previous = data[0]
for j in range(len(data)):
    tmp = 0
    for i in range(len(data)-1):
        if data[i] > data[i+1]:
            tmp=data[i]
            data[i] = data[i+1]
            data[i+1] = tmp
    print(data)
print("排序之后:",data)

八、正则表达式

正则表达式是一个特殊的字符序列,它能帮助你方便的检查一个字符串是否与某种模式匹配。

Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。

re 模块使 Python 语言拥有全部的正则表达式功能。

compile 函数根据一个模式字符串和可选的标志参数生成一个正则表达式对象。该对象拥有一系列方法用于正则表达式匹配和替换。

re 模块也提供了与这些方法功能完全一致的函数,这些函数使用一个模式字符串做为它们的第一个参数。

1.导入模块

1
import re

2.正则表达式匹配规则

1)正则表达式修饰符 - 可选标志

正则表达式可以包含一些可选标志修饰符来控制匹配的模式。修饰符被指定为一个可选的标志。多个标志可以通过按位 OR(|) 它们来指定。如 re.I | re.M 被设置成 I 和 M 标志:

修饰符 描述
re.I 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响 ^ 和 $
re.S 使 . 匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响 w, W, , B.
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理 解。

2)正则表达式模式

模式字符串使用特殊的语法来表示一个正则表达式:

字母和数字表示他们自身。一个正则表达式模式中的字母和数字匹配同样的字符串。

多数字母和数字前加一个反斜杠时会拥有不同的含义。

标点符号只有被转义时才匹配自身,否则它们表示特殊的含义。

反斜杠本身需要使用反斜杠转义。

由于正则表达式通常都包含反斜杠,所以你最好使用原始字符串来表示它们。模式元素(如 r'/t',等价于'//t')匹配相应的特殊字符。

下表列出了正则表达式模式语法中的特殊元素。如果你使用模式的同时提供了可选的标志参数,某些模式元素的含义会改变。(引用菜鸟教程)

模式 描述
^ 匹配字符串的开头
$ 匹配字符串的末尾。
. 匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...] 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k'
[^...] 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
re* 匹配0个或多个的表达式。
re+ 匹配1个或多个的表达式。
re? 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{ n}  
re{ n,} 精确匹配n个前面表达式。
re{ n, m} 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a| b 匹配a或b
(re) G匹配括号内的表达式,也表示一个组
(?imx) 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
(?-imx) 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
(?: re) 类似 (...), 但是不表示一个组
(?imx: re) 在括号中使用i, m, 或 x 可选标志
(?-imx: re) 在括号中不使用i, m, 或 x 可选标志
(?#...) 注释.
(?= re) 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
(?! re) 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功
(?> re) 匹配的独立模式,省去回溯。
w 匹配字母数字
W 匹配非字母数字
s 匹配任意空白字符,等价于 [ f].
S 匹配任意非空字符
d 匹配任意数字,等价于 [0-9].
D 匹配任意非数字
A 匹配字符串开始
匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。c
z 匹配字符串结束
G 匹配最后匹配完成的位置。
匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
B 匹配非单词边界。'erB' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
,  , 等. 匹配一个换行符。匹配一个制表符。等
1...9 匹配第n个分组的子表达式。
10 匹配第n个分组的子表达式,如果它经匹配。否则指的是八进制字符码的表达式。

正则表达式实例

字符匹配

实例 描述
python 匹配 "python".

字符类

实例 描述
[Pp]ython 匹配 "Python" 或 "python"
rub[ye] 匹配 "ruby" 或 "rube"
[aeiou] 匹配中括号内的任意一个字母
[0-9] 匹配任何数字。类似于 [0123456789]
[a-z] 匹配任何小写字母
[A-Z] 匹配任何大写字母
[a-zA-Z0-9] 匹配任何字母及数字
[^aeiou] 除了aeiou字母以外的所有字符
[^0-9] 匹配除了数字外的字符

特殊字符类

实例 描述
. 匹配除 " " 之外的任何单个字符。要匹配包括 ' ' 在内的任何字符,请使用象 '[. ]' 的模式。
d 匹配一个数字字符。等价于 [0-9]。
D 匹配一个非数字字符。等价于 [^0-9]。
s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ f v]。
S 匹配任何非空白字符。等价于 [^ f v]。
w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
W 匹配任何非单词字符。等价于 '[^A-Za-z0-9_]'。

3.具有的方法:

1)re.match函数

re.match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。。

函数语法

1
re.match(pattern, string, flags=0)
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

注:匹配成功re.match方法返回一个匹配的对象,否则返回None。

使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。

group(num=0) 匹配的整个表达式的字符串,group() 可以一次输入多个组号,在这种情况下它将返回一个包含那些组所对应值的元组。
groups() 返回一个包含所有小组字符串的元组,从 1 到 所含的小组号。

案例1:

1
2
3
4
5
6
7
8
9
10
11
12
import re
print(re.match('www', 'www.yaomr.com'))
显示如下:
<_sre.SRE_Match object; span=(0, 3), match='www'>
如果要显示匹配几个字符
print(re.match('www', 'www.yaomr.com').span())
效果:
0, 3)
如果匹配不到则返回:
print(re.match('www', 'ww.yaomr.com'))
显示:
None

案例2:

显示匹配结果:

 

1
2
3
4
5
6
7
8
9
10
import re
res = re.match('d+', '123uuasf')
if res:
    print (res.group())
else:
    print(None)
如果匹配成功显示:
123
如果匹配失败:
None

2)re.search方法

re.search 扫描整个字符串并返回第一个成功的匹配。函数语法同re.match方法

案例1:

1
2
print(re.search('www', 'www.yaomr.com').span())  # 在起始位置匹配
print(re.search('yao', 'www.yaomr.com').span())         # 不在起始位置匹配

返回:

案例2:

1
2
3
4
5
>>> obj = re.search('d+', 'u123uu888asf')
>>> print (obj)
<_sre.SRE_Match object; span=(1, 4), match='123'>
>>> obj.group()
'123'

3)re.match与re.search的区别

re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
import re
line = "Cats are smarter than dogs";
 
matchObj = re.match( r'dogs', line, re.M|re.I)
if matchObj:
   print "match --> matchObj.group() : ", matchObj.group()
else:
   print "No match!!"
 
matchObj = re.search( r'dogs', line, re.M|re.I)
if matchObj:
   print "search --> matchObj.group() : ", matchObj.group()
else:
   print "No match!!"

4)re.sub方法

Python 的re模块提供了re.sub用于替换字符串中的匹配项。

语法:

re.sub(pattern, repl,string, max=0)

返回的字符串是在字符串中用 RE 最左边不重复的匹配来替换。如果模式没有发现,字符将被没有改变地返回。

可选参数 count 是模式匹配后替换的最大次数;count 必须是非负整数。缺省值是 0 表示替换所有的匹配。

实例:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
import re
 
phone = "2004-959-559 # This is Phone Number"
 
# Delete Python-style comments
num = re.sub(r'#.*$', "", phone)
print "Phone Num : ", num
 
# Remove anything other than digits
num = re.sub(r'D', "", phone)   
print "Phone Num : ", num

案例:

1
2
3
4
content = "123abc456"
new_content = re.sub('d+', 'sb', content)
# new_content = re.sub('d+', 'sb', content, 1)
print new_content

5)group和groups

1
2
3
4
5
6
a = "123abc456"
print re.search("([0-9]*)([a-z]*)([0-9]*)", a).group()
print re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(0)
print re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(1)
print re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(2)
print re.search("([0-9]*)([a-z]*)([0-9]*)", a).groups()

6)findall(pattern, string, flags=0)

如果想要匹配到字符串中所有符合条件的元素,则需要使用 findall。

1
2
3
import re
obj = re.findall('d+', 'fa123uu888asf')
print obj

7)split(pattern, string, maxsplit=0, flags=0)

通过正则表达式将字符串分离。如果用括号将正则表达式括起来,那么匹配的字符串也会被列入到list中返回。maxsplit是分离的次数,maxsplit=1分离一次,默认为0,不限制次数。

1
2
>>> re.split('W+', 'Words, words, words.')
['Words', 'words', 'words', '']





迭代器提供两种方法:

(1)__next__() 返回迭代器的下一个元素