协程函数
yield的一种用法,只是换了一种形式,yield是把函数的结果变成一个生成器,
def func(count): print('start') while True: yield count count +=1 func(10) #有了yield后,就不会直接产生结果
g=func(10)
print(g) # 函数的结果变成了一个生成器
yield作用:
1.把函数的执行结果封装好__iter__和__next__,即得到一个迭代器
2.与return功能类似,都可以返回值,但不同的是,return只能返回一次值,而yield可以返回多次值
3.函数暂停与再继续运行的状态是由yield保存的
yield的表达式形式的应用:
def eater(name): print('%s 说:我开动啦' % name) while True: food=yield #一碰到yield就暂停住并且把yield后面的返回值返回当作next(ryan_g)的结果 print('% eat %s' % (name,food)) ryan_g=eater('ryan') # 函数的执行结果就是一个生成器
print(ryan_g) #这是一个生成器
next(ryan_g) # 第一次yield后面没有返回值
print(next(ryan_g)) #所以返回None
print('-------------->')
next(ryan_g) # 从上次暂停的位置继续往后走
print(next(ryan_g)) # ryan eat None # None
现在通过yield给food传值
def eater(name): print('%s 说:我开动啦' % name)
food_list=[] # 用来记录已经上过的菜 while True: food=yield food_list
food_list.append(food) print('% eat %s' % (name,food)) ryan_g=eater('ryan')
#第一阶段:初始化
next(ryan_g) # 等同于ryan.send(None)
print('--------->') #ryan说:我开动啦 # --------->
#第二阶段:给yield传值
ryan_g.send('骨头') #把'骨头'传给yield,赋值给food,继续往下执行,直到再次碰到yield,然后暂停并且把yield后的返回值当作本次调用的返回值 # ryan eat 骨头
print(ryan_g.send('骨头')) # ['骨头'] yield把函数暂停,并且把foodlist当作本次调用的返回值
print('--------->')
print(ryan_g.send('菜汤')) # ['骨头','菜汤']
print(ryan_g.send('包子')) #['骨头','菜汤','包子']
总结:
传参的3种方式
1.通过实参传给形参
2.装饰器的形式,在函数外包一层
3.在函数内部定义yield,等式的形式,函数执行不会有执行效果,得到一个生成器,用send的方式传值
好处:
在用传参的方式时,每次传参都要调用一次,调用完后传值进去,会产生局部名称空间,调用完后结束,下次再调用一次,再申请,再释放
而现在传一次参数,执行一次函数,通过send,但比起上面的好处是不用重复申请内存空间了,一直是基于上次来执行的,始终用的都是一个函数,
而且,在函数外部拿到一个函数的生成器,有了这个生成器就可以到处传,传给其他函数,把这些生成器写到另一个函数里,就是两个函数之间的交互
def eater(name): print(' %s 说:我开动啦' % name) food_list=[] while True: food=yield food_list food_list.append(food) print(' %s eat %s' % (name,food)) def producer(): ryan_g=eater('ryan') #函数的生成器 next(ryan_g) # 初始化 while True: food=input('>>: ').strip() if not food:continue print(ryan_g.send(food)) producer()
每次都要初始化,现在改为只管send,不管初始化的事情,使用装饰器(使其自动完成初始化--->next(ryan_g)),使 ryan_g=eater('ryan') 执行完的结果直接是 next之后的结果
def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def eater(name): print('%s 说:我开动啦' % name) food_list=[] while True: food=yield food_list food_list.append(food) print('%s eat %s' % (name,food)) ryan_g=eater('ryan') print(ryan_g.send('骨头'))
面向过程编程
核心是过程二字,过程即解决问题的步骤
优点:程序结构清晰,把复杂为提简单化,流程化
缺点:可扩展性差,一条流水线只是用来解决一个问题
实现-->grep -rl 'error' /dir/
建立如下文件,并且在a1.txt,a2.txt,b1.txt中写入error,其余的文件不写入error,随便写别的
import os
def init(func):
def wrapper(*args,**kwargs):
g=func(*args,**kwargs)
next(g)
return g
return wrapper
#第一阶段:找到所有文件的绝对路径
@init
def search(target):
while True:
filepath = yield
g=os.walk(filepath)
for pardir,_,files in g: # 父目录,子目录,文件
# print(pardir,files)
for file in files:
abspath=r'%s\%s' % (pardir,file)
target.send(abspath)
#第二阶段:打开文件
@init
def opener(target):
while True:
abspath=yield
with open(abspath,'rb') as f:
target.send((abspath,f))
#第三阶段:循环读出每一行内容
@init
def cat(target):
while True:
abspath,f=yield #(abspath,f)
for line in f:
res=target.send((abspath,line))
if res:break
#第四阶段:过滤
@init
def grep(pattern,target):
tag=False
while True:
abspath,line=yield tag
tag=False
if pattern in line:
target.send(abspath)
tag=True
#第五阶段:打印该行属于的文件名
@init
def printer():
while True:
abspath=yield
print(abspath)
g = search(opener(cat(grep('error'.encode('utf-8'), printer()))))
g.send(r'D:PyCharm_Projectsday7a')
递归调用
在调用一个函数的过程中,直接或间接的调用了函数本身
直接调用
def func(): print('from func') func() func()
间接调用
def foo(): print('from foo') bar() def bar(): print('from bar') foo() foo()
对递归做的限制
1.必须有一个明确的结束条件
2.每进入更深一层递归时,问题规模比上次都应有所减少
3.效率不高
# 取出列表中的所有元素,for 循环就没办法,因为不知道次数,这里用递归更合适,递归不用关心循环多少层,只需要控制好结束条件即可
l =[1, 2, [3, [4, 5, 6, [7, 8, [9, 10, [11, 12, 13, [14, 15,[16,[17,]],19]]]]]]] def search(l): for item in l: if type(item) is list: search(item) else: print(item) search(l)
二分法
#查找某一个数字是否在这个列表中 l = [1,2,5,7,10,31,44,47,56,99,102,130,240]
def binary_search(l,num): print(l)
if len(l) == 1:
if l[0] == num:
print('find it')
else:
print('not exists')
return mid_index=len(l)//2 mid_value=l[mid_index] if num==mid_value: print('find it') return if num > mid_value: l=l[mid_index:] if num < mid_index: l=l[:mid_index] binary_search(l,num) binary_search(l,32)
模块
import,导入模块做了哪些事?
1.执行源文件
2.产生一个源文件的全局名称空间
3.在当前位置拿到一个模块名,指向2创建的名称空间
import ... as ...
#mysql.py def sqlparse(): print('mysql sqlparse')
#oracle.py def sqlparse(): print('oracle sqlparse')
# test.py sql_type=input('sql_type: ') if sql_type == 'mysql': import mysql as sql elif sql_type == 'oracle': import oracle as sql sql.sqlparse() # 统一的调用方式
from ... import ...
优点:使用源文件内的名字时无需加前缀,使用方便
缺点:容易与当前文件的名称空间内的名字混淆
from ... import *
( *是除了以_开头的,_只对*有用,若是from ... import _money,则无用,也可以导入)
#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
#from ... import.py from spam import * print(money) # 报错
# spam.py __all__ = ['money','x'] # 只对*起作用,别的函数只能导入__all__定义的变量和函数
money=1000
def _read1():
print('spam->read1->money',money)
def read2():
print('spam->read2 calling read')
read1()
def change():
global money
money=0
#from ... import.py from spam import * print(money) print(x)
模块只在第一次导入时才会执行,之后的导入都是直接引用内存已经存在的结果
import sys print('spam' in sys.modules) #存放的是已经加载到内存中的模块
模块的搜索路径
先从内存中找,再从内置模块中找,最后从硬盘中找(sys.path)
如何区分python文件的两种用途
1.文件当作脚本运行时,__name__等于__main__
2.文件当作模块被加载运行时,__name__等于模块名
包
Logging模块
日志级别
CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = 20
DEBUG = 10
import logging logging.info('info') logging.warning('warning') logging.error('error') logging.critical('critical')
# 这种方式日志默认写到了终端上
把日志写到文件中
软件开发规范
ATM ---->项目名 bin目录----->放执行文件 start.py ---> 执行此文件时要保证bin,conf,log,lib,db,core目录下的所有文件都能被找到,因此ATM目录需要加入到环境变量中去 conf目录--->放配置文件 settings.py log目录 access.log lib目录 ----->存放模块,包,文件,自己开发的功能模块 glance包 common.py --> 模块 db目录 core目录 ---> 核心逻辑 src.py ---->通过start.py来调用核心逻辑
README ----> 软件的介绍,如何使用
#src.py -----> 此文件不能直接运行 from conf import settings
def search():
print('search',settings.x)
def run(): print('''
1 查询
2 购物
3 转账
''')
while True:
choice=input('>>: ').strip()
if not choice:continue
if choice == '1':
search()
elif choice == '2':
pass
elif choice == '3';
pass
else:
pass
#src.py import sys,os BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASE_DIR) from core import src if __name__ == '__main__': src.run()
#settings.py x=1