协程,递归,二分法,模块,包,软件开发规范

协程函数

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
原文地址:https://www.cnblogs.com/Ryans-World/p/7258380.html