Python【day4】:Python学习(迭代器、生成器、装饰器、递归、算法基础:二分查找、二维数组转换、正则表达式等介绍)

迭代器

#1迭代器
# 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
# 不能随机访问集合中的某个值 ,只能从头到尾依次访问
# 访问到一半时不能往回退
# 便于循环比较大的数据集合,节省内存

a = iter([1,2,3,4])
# iter(iterable) -> iterator
# iter(callable, sentinel) -> iterator
#
# Get an iterator from an object. In the first form, the argument must
# supply its own iterator, or be a sequence.
# In the second form, the callable is called until it returns the sentinel.
print(a) #<list_iterator object at 0x0000000000644B00> #无法直接从迭代器取值,迭代器的创建
print(a.__next__()) #1
'Return the next item from the iterator. When exhausted, raise StopIteration'
a.__next__()
print(a.__next__()) #3
print(a.__next__()) #4
# print(a.__next__()) #StopIteration

生成器
# 生成器generator  
# 定义: 如果函数中包含yield语法,那这个函数就会变成生成器,这个函数调用返回return的是一个迭代器
# 适用场景:在单线程-串行的程序中,函数执行的过程中,暂停一下(等待函数返回的同时),同时去干别的事情,实现串行下的并发
# 作用:
# 这个yield的主要效果呢,就是可以使函数中断,并保存中断状态,中断后,代码可以继续往下执行,过一段时间还可以再重新调用这个函数,
# 从上次yield的下一句开始执行。 另外,还可通过yield实现在单线程的情况下实现并发运算的效果


#1取钱的例子
def cash_out(amount):
while amount>0:
amount-=100
# return 100
yield "暂停一下" #函数是生成器,函数返回迭代器
print("余额是:",amount)
# print("来取钱")
atm = cash_out(200)
print(cash_out(200)) #<generator object cash_out at 0x000000000073BF68>
# cash_out(300)
print(type(cash_out(200))) #<class 'generator'>
print(atm.__next__()) #暂停一下 返回12行
print("去理发") #相当于在函数执行的过程中,先yield暂停一下,去干别的事情-理发,干完别的事情后,回来接着刚刚暂停的地方继续执行
print(atm.__next__()) #暂停一下 返回13行
# print("去理发2")
# atm.__next__() #StopIteration
# print("去理发3")

# 通过yield实现在单线程的情况下实现并发运算的效果
# 2吃包子 生产者消费者模式

import time
def consummer(name):
print("%s开始吃包子" % name)
while True:
baozi =yield #yield既可以返回暂停一下,也可以接受send发过来的值,包含yield就是生成器,方法next 暂停到19行
print("包子%s来了,被%s吃了" % (baozi,name))

def producer(name2):
c1 = consummer("jack") #调用函数1
c2 = consummer("tom")
c1.__next__() #consummer函数返回的是迭代器 回到9行
c2.__next__()
print("开始做包子")
for i in range(10):
time.sleep(1) #每一秒钟做2个包子 如果注释这行的话,就会一次全部输出
print("%s做了2个包子" % name2)
c1.send("hi")
c2.send(i) #回到12行

producer("alex") #调用函数2

装饰器
#需求,登录首页不需要用户名密码验证,不必加装饰器,但是查看tv或者movie页面需要用户名密码验证,需要加上装饰器

#1函数不带参数的装饰器
def login(func): #func=tv
def inner(): #inner() = tv()
print("用户名密码验证") #用户名密码验证功能-函数
func() #func() = tv() #执行tv查看函数 15行
return inner #inner = tv

def home(name): #登录首页,不需要用户名密码验证
print("welcome [%s] to home page" % name)

@login #相当于 tv = login(tv) tv=inner
def tv(): #查看tv页面--不带参数
print("welcome to TV page")

def movie(name): #查看电影页面
print("welcome [%s] to movie page" % name)

tv() #执行不带参数,业务方调用

# 输出
# 用户名密码验证
# welcome to TV page

#2函数带参数的装饰器
def login(func): #func=tv
def inner(*args,**kwargs): #inner() = tv()
print("用户名密码验证") #用户名密码验证功能-函数
func(*args,**kwargs) #func() = tv() #执行tv查看函数 15行
return inner #inner = tv

def home(name): #登录首页,不需要用户名密码验证
print("welcome [%s] to home page" % name)

@login #相当于 tv = login(tv) tv=inner
def tv(name,pwd): #查看tv页面--带参数
print("welcome %s to TV page,%s" %(name,pwd))

def movie(name): #查看电影页面
print("welcome [%s] to movie page" % name)

# tv(name="jack",pwd="123") #执行带参数字典,业务方调用
tv("jack","123") #执行带参数-字符串,业务方调用
# tv(*["jack","123"]) #执行带参数-列表1星,业务方调用
# tv(**{"name":"jack","pwd":"123"}) #执行带参数-字典2星,业务方调用 相当于将字典的2个key对应的value作为2个参数分别传入,对号入座

#需求,要求用户在查看tv页面前先登录,查看tv页面后退出登录
#实现方式1:给tv函数,写2个装饰器
#实现方式2:给tv函数,写1个默认装饰器,写2个函数,将这2个函数作为参数传入默认装饰器(带参数的装饰器)
# (如果要给tv函数添加10个功能,在不动源代码的情况下,需要些10个装饰器;由于写函数的成本低于写装饰器,所有出现了带参数的装饰器
# 通过1个带参数的装饰器,加上10个函数,将这10个函数作为参数传入默认装饰器(带参数的装饰器))

def Login(request,kargs): #登录,执行功能函数前,先登录
print('before') #相当于在查看tv页面前,先执行登录用户名密码验证

def Errorhandle(request,kargs): #执行功能函数后,处理错误或者提示错误
print('after') # #相当于在查看tv页面后,执行收尾工作(例如退出用户名登录)


#3默认装饰器(带参数的装饰器)--一个函数被多个装饰器装饰(这里不必写2个装饰器,只需要写2个函数即可,4,7行)
def Filter(before_func,after_func):
def outer(main_func): #func=tv
def inner(request,kargs): #inner() = tv()
# print("用户名密码验证") #用户名密码验证功能-函数
# func(request,kargs) #func() = tv() #执行tv查看函数 15行
before_result = before_func(request,kargs) #登录密码验证
if(before_result != None): #登录有return,说明登录失败,自己定义的,功能函数不执行
return before_result; #返回登录错误信息

main_result = main_func(request,kargs) #业务函数 index
if(main_result != None): #业务函数有return,说明业务函数失败,自己定义的,功能函数不执行
return main_result; #返回业务函数错误信息

after_result = after_func(request,kargs) #收尾函数
if(after_result != None): ##执行业务函数后,处理一些收尾工作
return after_result; #返回收尾函数错误信息
return inner #inner = tv
return outer

def home(name): #登录首页,不需要用户名密码验证
print("welcome [%s] to home page" % name)

@Filter(Login, Errorhandle) #相当于 tv = login(tv) tv=inner 装饰器带参数
#第一步,和装饰器无关,将2个函数作为参数传入到12行,返回outer,剥离第一层def(装饰器带参数)
#第二步,@outer 相当于main_func = outer(main_func) 返回inner 这个就是普通的装饰器了
def tv(name,pwd): #查看tv页面--带参数
print("welcome %s to TV page,%s" %(name,pwd))

def movie(name): #查看电影页面
print("welcome [%s] to movie page" % name)

# tv(name="jack",pwd="123") #执行带参数字典,业务方调用
tv("jack","123") #执行带参数-字符串,业务方调用
# tv(*["jack","123"]) #执行带参数-列表1星,业务方调用
# tv(**{"name":"jack","pwd":"123"})

递归
# 递归
#
# 特点#
# 递归算法是一种直接或者间接地调用自身算法的过程。在计算机编写程序中,递归算法对解决一大类问题是十分有效的,
# 它往往使算法的描述简洁而且易于理解。
# 递归算法解决问题的特点:
# (1) 递归就是在过程或函数里调用自身。
# (2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。

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

#例子1
def calc(n):
pass
print(n) # 10 5 2.5 1.25
if n/2>1: #递归退出条件
ret=calc(n/2) #自己调用自己,每次都减半
print(ret) #1.25 2.5 5 如果没有return 这里3次都会返回None
# print(n) #1.25 2.5 5 10
return n #有return的时候,退出递归的时候,依次返回给上一层;没有return,退出递归的时候,默认返回None给上一次
# return None
calc(10)

#例子2
# 斐波那契数列(前2个数的和等于第三个数)指的是这样一个数列
# 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
def func(arg1,arg2,stop):
if arg1 ==0:
print(arg1,arg2) #0 1
arg3 = arg1+arg2
print(arg3) #1
# func(arg2,arg3) #不退出
if arg3<stop: #递归退出条件
func(arg2,arg3,stop) #自己调用自己,调用完毕才会到26行,return默认返回None,退出递归给上次返回None
print(arg3)
return arg3 #退出时,每次返回给上一层
# return None
# func(0,1,5)
print(func(0,1,5))

#例子3-二分算法
# 实现
# 1. 通过递归实现2分查找
#   现有列表 primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41,
# 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97], 要求用最快的方式 找出23


#方法1
def binary_search(data_list,find_num):
# mid_pos = int(len(data_list) /2 ) #find the middle position of the list
mid_pos = int(len(data_list) /2 )
mid_pos = int(len(data_list) //2 ) #地板除
mid_val = data_list[mid_pos] # get the value by it's position
print(data_list)
if mid_val == find_num:
print("Find ", find_num)
return #这个return必须加上,代表找到后,return None函数终止,如果不加上会出现数字1查找错误
if int(len(data_list)/2) == 0: #递归退出条件
print("cannot find [%s] in data_list" %find_num)
else: #计算器也是这个模式
if mid_val > find_num: # means the find_num is in left hand of mid_val
print("[%s] should be in left of [%s]" %(find_num,mid_val))
# return binary_search(data_list[:mid_pos],find_num)
binary_search(data_list[:mid_pos],find_num)
elif mid_val < find_num: # means the find_num is in the right hand of mid_val
print("[%s] should be in right of [%s]" %(find_num,mid_val))
# return binary_search(data_list[mid_pos:],find_num)
binary_search(data_list[mid_pos:],find_num)

if __name__ == '__main__':
# primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
primes=list(range(1,100,2)) #定义目标列表
res=binary_search(primes,1)
print(res)

#方法2
def binSearch(lst, item):
mid = len(lst) //2
found = False
if lst[mid] ==item:
found = True
print("找到了",item)
return found
return
if mid == 0:
#mid等于0就是找到最后一个元素了。
found = False
print("没有找到")
return found
else:
if item > lst[mid]: #找后半部分 #
print(lst[mid:])
return binSearch(lst[mid:], item)
else:
return binSearch(lst[:mid], item) #找前半部分测试通过。
print(lst[:mid])

primes=list(range(1,100,2))
ret1 =binSearch(primes,3)
print(ret1)
# ret = binSearch([2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
# 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97],1)
# print(ret)

算法-二维数组转换
# 要求:生成一个4*4的2维数组并将其顺时针旋转90度
# for i in range(4):
# print(i)
data = [i for i in range(4) ]
# print(data) #[0, 1, 2, 3] 将0,1,2,3这四个数字遍历后依次添加到列表中,存在变量data

data2 = [i for i in range(4) for j in range(4)]
# print(data2) #[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]

data3 = [[i for i in range(4)] for j in range(4)]
# print(data3) #[[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]
#将列表[0,1,2,3]整体作为一个元素连续4次添加到列表中,存在变量data3

#1二维数组
data3 = [[i for i in range(4)] for j in range(4)]
print(data3)
# for i in data3:
# print(i)

#4*4的2维数组的原始样子
[0, 1, 2, 3]
[0, 1, 2, 3]
[0, 1, 2, 3]
[0, 1, 2, 3]

# 顺时针旋转90度后的期望样子
[0, 0, 0, 0]
[1, 1, 1, 1]
[2, 2, 2, 2]
[3, 3, 3, 3]

# print(len(data3)) #4
# for j in data3[0]:
# print(j) #0 1 2 3

#方法
for i in range(len(data3)): #遍历大列表的每个元素(每行)
for j in data3[i]: #遍历小列表的每个元素--每行的4个元素
# pass #实现行列的互换,通过中间遍历temp实现
temp = data3[i][j] #temp = data3[0][1] 第一行存入temp
data3[i][j] = data3[j][i] #data3[0][1]= data3[1][0] 第一列替换第一行
data3[j][i] = temp #temp(里面存的是第一行) 替换第一列

for i in data3: #遍历含有4个小列表的大列表
print(i)
#输出
[0, 0, 0, 0]
[1, 1, 1, 1]
[2, 2, 2, 2]
[3, 3, 3, 3]

正则表达式
import re

# re模块用于对python的正则表达式的操作。
#
# 字符:
#
#   . 匹配除换行符以外的任意字符
#   \w 匹配字母或数字或下划线或汉字
#   \s 匹配任意的空白符
#   \d 匹配数字
#   \b 匹配单词的开始或结束
#   ^ 匹配字符串的开始
#   $ 匹配字符串的结束
#
# 次数:
#
#   * 重复零次或更多次
#   + 重复一次或更多次
#   ? 重复零次或一次
#   {n} 重复n次
#   {n,} 重复n次或更多次
#   {n,m} 重复n到m次

# 正则表达式常用6种操作
# match 从头匹配
# search 匹配左边第一个满足要求的
# findall 匹配所有满足要求的存到列表
# sub 替换,最后一个参数可以指定最大替换次数,不写的话,默认全部替换,
# 写1的话,以左边第一个符合要求的字符串作为被替换对象
# split 拆分,最后一个参数可以指定最大替换次数,不写的话,默认全部替换,
# 写1的话,以左边第一个符合要求的字符串作为分隔符

#1从头匹配,匹配单个
#1 re.match(pattern, string,flags=0) # 从头匹配
#第一个参数是匹配规则--正则 第二个参数是待查找的字符串
"""Try to apply the pattern at the start of the string, returning
a match object, or None if no match was found."""
obj = re.match('\d+', '123uuasf') #匹配字符串'123uuasf'开头的一个或者多个数字
if obj:
print(obj.group()) #123 group返回1个或者多个匹配到的子字符串

#group和groups的区别
#group group返回1个或者多个匹配到的子字符串
# Return one or more subgroups of the match.

#groups groups将返回的1个或者多个匹配到的子字符串存到一个元组
# Return a tuple containing all the subgroups of the match, from 1 up
# to however many groups are in the pattern.

#2 根据模型去字符串中匹配指定内容,匹配单个(返回左边第一个符合要求的)
# re.search(pattern, string)    # 匹配整个字符串,直到找到一个匹配
#第一个参数是匹配规则--正则 第二个参数是待查找的字符串
"""Scan through string looking for a match to the pattern, returning
a match object, or None if no match was found."""
obj = re.search('\d+', 'u123uu888asf') #匹配字符串'u123uu888asf'中左边第一个满足要求的--一个或者多个数字
if obj:
print(obj.group()) #123

#3 findall(pattern, string, flags=0) 匹配到字符串中所有符合条件的元素
#第一个参数是匹配规则--正则 第二个参数是待查找的字符串
# 上述两种方式均用于匹配单值,即:只能匹配字符串中的一个,
# 如果想要匹配到字符串中所有符合条件的元素,则需要使用 findall。
"""Return a list of all non-overlapping matches in the string.
If one or more capturing groups are present in the pattern, return
a list of groups; this will be a list of tuples if the pattern
has more than one group.
Empty matches are included in the result."""
# re.findall()# 找到所有要匹配的字符并返回列表格式
obj = re.findall('\d+', 'fa123uu888asf') #匹配字符串'fa123uu888asf'中所有满足要求的--一个或者多个数字存到列表中
print(obj) #['123', '888']
m = re.findall("[0-9]", "alex1rain2jack3helen rachel8")
#匹配字符串"alex1rain2jack3helen rachel8"中所有的数字,作为元素存到列表中
print(m) #输出:['1', '2', '3', '8']

#4替换re.sub --相比于str.replace功能更加强大
# re.sub(pattern, repl, string, count,flag)    # 替换匹配到的字符
#参数1:匹配规则-正则;参数2:替换符号(用什么替换);参数3:待查找字符串;4、匹配的最多次数(不写的话,默认全部替换)
m=re.sub("[0-9]","|", "alex1rain2jack3helen rachel8",count=2 )
#将字符串中左边前2个找到数字,替换成竖杠
print(m)# 输出:alex|rain|jack3helen rachel8 

# sub(pattern, repl, string, count=0, flags=0)
# 用于替换匹配的字符串
content = "123abc456"
new_content = re.sub('\d+', 'sb', content) #sbabcsb
#将字符串中匹配到的多个数字,全部替换成"sb"
new_content2 = re.sub('\d+', 'sb', content, 1)
print(new_content)
print(new_content2) #sbabc456 #将字符串中匹配到的多个数字,不是全部替换成"sb",而是只把第一个满足要求的替换成"sb"

#5分割re.split--相比于str.split更加强大
# split(pattern, string, maxsplit=0, flags=0)
# re.split()  # 将匹配到的格式当做分割点对字符串分割成列表
m = re.split("[0-9]", "alex1rain2jack3helen rachel8")
#将字符串中匹配到的所有数字,当做分隔符,将字符串拆分成小字符串,作为元素,添加到列表
print(m) #输出: ['alex', 'rain', 'jack', 'helen rachel', ''] 最后是一个空白""

# 根据指定匹配进行分组
content = "'1 - 2 * ((60-30+1*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2) )'"
new_content = re.split('\*', content) #以星号*作为分隔符(转义后),拆分字符串
#["'1 - 2 ", ' ((60-30+1', '(9-2', '5/3+7/3', '99/4', '2998+10', '568/14))-(-4', '3)/(16-3', "2) )'"]
new_content2 = re.split('\*', content, 1)
print(new_content)
print(new_content2) #以左边匹配到的第一个星号*作为分隔符(转义后),拆分字符串成2部分,添加到列表
#["'1 - 2 ", " ((60-30+1*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2) )'"]

content = "'1 - 2 * ((60-30+1*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2) )'"
new_content = re.split('[\+\-\*\/]+', content) #以运算符+-*/作为分隔符(转义后),拆分字符串
#["'1 ", ' 2 ', ' ((60', '30', '1', '(9', '2', '5', '3', '7', '3', '99', '4', '2998', '10', '568', '14))', '(', '4', '3)', '(16', '3', "2) )'"]
new_content2 = re.split('[\+\-\*\/]+', content, 1)
print(new_content)
print(new_content2) #以左边第一个运算符+-*/作为分隔符(转义后),拆分字符串成2部分,作为元素一次添加到列表
#["'1 ", " 2 * ((60-30+1*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2) )'"]


inpp = '1-2*((60-30 +(-40-5)*(9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2))'
print(eval(inpp)) #15619067.885714287
inpp = re.sub('\s*','',inpp) #去掉表达式的空格 \s匹配任意的空白符 *0个或者多个
print(inpp) #1-2*((60-30+(-40-5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))

new_content = re.split('\(([\+\-\*\/]?\d+[\+\-\*\/]?\d+){1}\)', inpp, 1)
#以左边第一个括号开头,括号结尾,中间不包含括号的字符串'-40-5' 作为分隔符将inpp字符串拆分长前中后 3部分,作为元素依次添加到列表
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))']

#注意:如果split中匹配的字符是单个字符(例如:*),就会拆分成2部分,星号不算,星号前是一部分,星号后是一部分
# 如果split中匹配的字符不是单个字符(例如:('-40-5')),就会拆分成3部分 ('-40-5')之前是一部分
# ('-40-5')之后是一部分,'-40-5'是中间的一部分,这里中间的一部分不包含小括号
 
 
 
 
posted @ 2016-01-25 15:05  王同佩  阅读(200)  评论(0)    收藏  举报