运维小白的Python之路(三)

迭代器和生成器

迭代器

迭代器的定义:从对象的第一个元素开始访问,直到最后一个元素。迭代器不需要准备好所有的元素,适用于读取较大的文件。
特点:
1、不需要关心迭代器的内部结构,仅需通过next()(在python2.7中是next())方法,不断取下一个内容。
2、不能随机访问集合中的某个值,只能从头到尾访问
3、访问一半时不能后退
4、便于循环比较的数据集合,可以节省内存

创建迭代器

names = iter(['a', 'b', 'c']) #创建
print(name.__next__()) #调用
#在迭代器遍历完最后一个元素的时候,再次调用迭代器回报错

文件对象在创建的时候就包含了迭代器的功能。在使用read()或者readlins() 的时候,会出现文件过大导致程序出现卡顿的情况。可以使用文件对象的迭代器功能来避免这种情况

f = open("1.txt")
for i in f:
    ...

生成器的使用

生成器(generator)的定义:一个函数在调用时返回一个迭代器,这个函数就叫做生成器。如果函数包含yield语法,这个函数就会变成生成器。

#生成器函数,调用一次就减少100
def money(amount):
    while amount > 0:
        amount -= 100
        yield 100 #通过yield返回100
        print("get 100$")
atm = money(500)
print(atm.__nexy__())         

使用yield单线程的异步并发效果

import time
def consumer(name):
    print("%s准备吃包子" % name)
    while 1:
        baozi = yield #这里的yield可以接收sent发来的值
        print("包子%s被%s吃了" % (baozi, name))
def proudcer():
    c1 = consumer("A")
    c2 = consumer("B")
    c1.__next__()
    c2.__next__()
    print("正在开始做包子")
    for i in rabge(10):
        time.sleep(1)
        print("已经做了两个包子")
        c1.send(i) #这里的sent会把i的值传递给yield
        c2.send(i)
proudcer("xiaoming")

装饰器

写代码的时候,要遵循开发封闭原则,虽然这个是适用于面向对象开发的,但是同样也适用于函数式开发。简单来说,它规定已经实现的功能代码不允许被修改,但是可以被修改。即:
封闭:已经实现的代码模块
开放:对扩展开发

装饰器实现的原理

def w1(func):
    def inner()
        ...
        ...
        ...
        return func()
    return inner
@w1
def f1():
    print("f1")

如果看不明白,可以先参考下面的代码

def login(func):
    print("check user")
    return func
def tv():
    print("wellcom tv")
tv = login(tv) #先执行login函数,把tv函数传进login函数中。login 函数再把tv函数返回再赋值给tv。只有在函数名后面加括号的情况下才会执行函数,这里的tv并没有执行。
tv() #执行tv函数。这样就可以先执行login函数,在执行tv函数

把上面的例子转化成装饰器的例子

def login(func):
    def inner(arg):
        print("check user")
        func()
    return inner
@login
def tv(name):
    print("wellcon to %s" % name)
tv("xiaoming")

装饰器的进一步详解

先举个例子

#!/usr/vin/env python
def w1(func):
    def run(the_name):
            p1()
            return func(the_name)
    return run
def p1():
    print("This is zhuang_shi_pi")
@w1
def f1(name):
    print("My name is %s" % name)
    return 3
a = f1("xiaoming")
print(a)
This is zhuang_shi_pi
My name is xiaoming
3

当python在执行的时候,扫描到“@w1”的时候会做以下事情:
1、执行w1函数(如果只是w1的话只是引用了函数,但是在w1前面加了@,就执行函数了)。并把下面的f1当作w1的参数传入。
2、w1函数会有返回值,装饰器会在这个时候把f1函数重新赋值,把w1函数的返回值赋值给f1,就相当于把f1变成了run()

更复杂的装饰器

这个例子是实现了可以穿参数的装饰器

#!/usr/bin/env python
def before():
    print("This is before")

def after():
    print("This is after")

def zhuangshiqi(func_1, func_2):
    def run(f1):
        def p1(name):
            func_1()
            n = f1(name)
            func_2()
            return n
        return p1
    return run
@zhuangshiqi(before, after)
def f1(name):
    print("My name is %s" % name)
    return 3

a = f1("xiaoming")
print(a)

装饰器在这里做了下面的事情:
1、当扫描到@zhuangshiqi(before, after)的时候,由于zhuangshiqi函数后面加了括号,所以这里先完成装饰器的工作,而是现在执行zhuangshiqi函数
2、之后zhuangshiqi函数后,函数会有返回值,返回的是run函数
3、run函数会结合前面的@新城装饰器,会把下面的f1函数当做run函数的参数传进,并把新的返回值p1重新赋值给f1函数
4、在执行f1的时候就等于执行了p1函数

递归函数

递归函数就是直接或是间接的调用自身算法的过程
特点:
1、递归就是在过程或函数里调用自身
2、在递归调用的时候,必须有一个明确的递归条件作为递归出口。
3、递归算法看上去很简洁,但是效率低,一般不提倡使用。
4、递归算法在调用的过程中,系统会为每一层的返回点、局部变量等开辟了栈来存储,多次递归可能造成栈的溢出。(在python中不能超过99层)

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

递归小例子

#!/usr/bin/env python
def digui():
    print("before n:", n)
    if (n/2 > 1):
        res = digui(n/2)
        print("the res:",res)
    print("after n:",n)
    return n
digui(10)

第二个小例子

#!/usr/bin/env python
def digui(arg1, arg2, stop):
    if arg1 == 0:
        print(arg1)
        print(arg2)
    arg3 = arg1 + arg2
    if arg3 < stop
        print(arg3)
        digui(arg2, arg3, stop)
digui(0,1,30)

利用递归函数实现二分法

#!/usr/bin/env python
def er_fen(shu_ji, found):
    mid = int(len(shu_ju)/2)
    if len(shu_ju) >= 1:
        if shu_ju[mid] > found:
            print(shu_ji[mid])
            er_fen(shu_ju[:mid], found)
        elif shu_ju[mid] < found:
            print(shu_ju[mid])
            er_fen(shu_ju[mid:], found)
        else:
            print(shu_ju[mid],"is found")
    else:
            print("The %s is not found" % found)
shu_ju = range(1,700,4)
er_fen(shu_ju, 400)

通过算法实现一个二维数组的90度旋转

#!/usr/bin/env python
#列表解析
a = [[i for i in range(4)] for j in range(4)]
for i in a:
    print(i)
#通过嵌套循环实现位置的调换
for x in range(len(a)):
    for y in range(x):
        if x == z:
            continue
        a[x][y], a[y][x] = a[y][x], a[x][y]

for i in a:
    print(i)

正则表达式

Python的正则表达式是通过re模块实现的

模式与描述

^   匹配字符串的开头
$  匹配字符串的末尾。
.   匹配任意字符,除了换行符,当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  匹配任意空白字符,等价于 [\t\n\r\f].
\S  匹配任意非空字符
\d  匹配任意数字,等价于 [0-9].
\D  匹配任意非数字
\A  匹配字符串开始
\Z  匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。c
\z  匹配字符串结束
\G  匹配最后匹配完成的位置。
\b  匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
\B  匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
\n, \t, 等.  匹配一个换行符。匹配一个制表符。等
\1...\9     匹配第n个分组的子表达式。
\10     匹配第n个分组的子表达式,如果它经匹配。否则指的是八进制字符码的表达式。
import re
m = re.match("要匹配的字符", "原字符")

re.match(pattern, string, flags=0)

匹配字符串并返回一个对象,要显示匹配的字符需要使用group()方法
匹配一个特定的字符串

import re
m = re.match("abc", "abcdsasfw")#返回的是一个正则对象,需要调用方法将其显示
print(m.group())
abc

匹配字符串第一个字符是数字(如果不是数字报错)

import re
m = re.match("[0-9]", "7554abcdsasfw")
print(m.group())
7
#############################################
import re
m = re.match("[0-9]", "a7554abcdsasfw")
print(m.group())

/usr/bin/python3 /root/PycharmProjects/s12/Day4/zhengze.py
Traceback (most recent call last):
  File "/root/PycharmProjects/s12/Day4/zhengze.py", line 3, in 
    print(m.group())
AttributeError: 'NoneType' object has no attribute 'group'
import re
m1 = re.match("[0-9][0-9]", "7554abcdsasfw") #匹配前两的字符是数字
m2 = re.match('[0-9]{0,10}', '7554abcdsasfw') #匹配前0~10个是数字的字符串
m3 = re.match('[0-9]{10}', "7554abcdsasfw")#匹配前十位都是数字的字符串(在这里没有匹配到,会报错)
print(m1.group())
print(m2.group())
print(m3.group())
75
7554
Traceback (most recent call last):
  File "/root/PycharmProjects/s12/Day4/zhengze.py", line 7, in 
    print(m3.group())
AttributeError: 'NoneType' object has no attribute 'group'

re.compile(pattern, flags=0)

生成要匹配对象的正则
如果只用match的话,每次都要对正则表达是进行编译,但是使用了compile后,只编译一次就可以了

import re
a = re.compile("^[0-9]*")
m = a.match("123asdzxc")
print(m.group())

re.findall(pattern, string, flags=0)

匹配字符串内所有的字符,并返回一个数组

import re
m1 = re.findall("[0-9]{0,10}", "7554abcd12sa343sfw")
print(m1)
#这里匹配的是0~10个连续的数字,空的字符串代表匹配的0次
['7554', '', '', '', '', '12', '', '', '343', '', '', '', '']
########################################################
import re
m1 = re.findall("[0-9]{1,10}", "7554abcd12sa343sfw")
print(m1)
['7554', '12', '343']

匹配1~10个连续的大小写字母

import re
m1 = re.findall("[a-zA-Z]{1,10}", "7554abcd12sa343sfw")
print(m1)
['abcd', 'sa', 'sfw']

匹配0到多个任意字符

m1 = re.findall(".*", "7554abcd12sa343sfw")
print(m1)
['7554abcd12sa343sfw', ''] #0个也被匹配,所以有个空字符

匹配1到多个字符

import re
m1 = re.findall(".+", "7554abcd12sa343sfw")
print(m1)
['7554abcd12sa343sfw']

re.search(pattern, string, flags=0)

查找全部,找到第一个

import re
m1 = re.search("[0-9][0-9]", "7554abcd12sa343sfw")
print(m1.group())
75
##########################################################
import re
m1 = re.search("[0-9][0-9]", "ddd7554abcd12sa343sfw")
print(m1.group())
75

sub(pattern, repl, string, count=0, flags=0)

替换所有匹配的

import re
m1 = re.sub("[0-9][0-9]","#" ,"ddd7554abcd12sa343sfw")
#将两个连续式数字的字符替换成#号
print(m1)
ddd##abcd#sa#3sfw
########################################################
import re
m1 = re.sub("[0-9][0-9]","#" ,"ddd7554abcd12sa343sfw", count=2)
#只替换前两个,注意:要想要匹配最后两个,只能先通过findall找到所有,在进行匹配
print(m1)

re.split(pattern, string, maxsplit=0, flags=0)

可以根据正则表达式将字符串拆分,maxsplit是拆分的次数
注:re.split可以用分隔符将字符串拆分,re.findall可以找到所有的分隔符。这两个方法可以结合使用

import re
inpp = '1-2*((60-30 +(-40-5)*(9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2))'
inpp = re.sub('\s*','',inpp)
new_content = re.split('\(([\+\-\*\/]?\d+[\+\-\*\/]?\d+){1}\)', inpp, 1)
print(new_content)  
['1-2*((60-30+', '-40-5', '*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))']

正则表达式的一些小例子

匹配IP地址

a = "192.168.96.22"
m = re.match('([0-9]{1,3}\.){1,3}\d{1,3}', a)
print(m.group())

匹配手机号

a = "asd jxzbx wdasc scacas 13804471259 zxh dwdsc asdsaf"
m = re.search("(1)([358]\d{9})", a)
print(m.group())

分组匹配

a = "wanda,tongzhoubeiyuan: 010-84756963"
m = re.search("(\w+),(\w+): (\S+)", a)
print(m.group())
print(m.group(1))
print(m.group(2))
print(m.group(3))
wanda,tongzhoubeiyuan: 010-84756963
wanda
tongzhoubeiyuan
010-84756963
a = "wanda,tongzhoubeiyuan: 010-84756963"
m = re.search("(?P\w+),(?P\w+): (?P\S+)", a)
print(m.group())
print(m.group('a'))
print(m.group('b'))
print(m.group('c'))
wanda,tongzhoubeiyuan: 010-84756963
wanda
tongzhoubeiyuan
010-84756963

匹配email

a = "[email protected]"
m = re.search("[0-9a-zA-Z]{1,25}@[0-9a-zA-Z]{1,10}\.[0-9a-zA-Z.]{1,10}", a)
print(m.group())

你可能感兴趣的:(python知识点)