1. 协程函数
1.1 yield基本用法
yield作用
1.把函数的执行结果封装好,即封装__iter__和__next__,即得到一个迭代器
2.与return功能类似,都可以返回值,但不同的是,return只能返回一次值,而yield可以返回多次值
3.函数暂停与继续运行的状态是由yield保存
示例
def func(count): print("start") while True: yield count count += 1 g = func(10) print(next(g)) print(next(g))
1.2 通过yield向函数传参数
通过yield方法send,向yield暂停的位置进行传值
表达式形式的yield应用:例如food = yield food_list;需要分为两个阶段
1.初始化 next(jack_g) 等价于jack_g.send(None)
2.给yield传值 jack_g.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)) jack_g = eater('jack') # 第一阶段:初始化 next(jack_g) # jack_g.send(None) # 第二阶段:给yield传值 print(jack_g.send('骨头')) #1 先给当前暂停位置的yield传骨头 #2 继续往下执行,直到再次碰到yield,然后暂停并且把yield后的返回值当做本次调用的返回值 print(jack_g.send('菜汤')) print(jack_g.send('狗肉包子'))
1.3 生产者、消费者
生产者
def consumer(name): print('%s 说:我开动啦' %name) food_list = [] while True: food = yield food_list food_list.append(food) #['骨头','菜汤'] print('%s eat %s' %(name,food))
消费者
def producer(): jack_g = consumer('jack') # 第一阶段:初始化 next(jack_g) # 第二阶段:给yield传值 while True: food = input('>>: ').strip() if not food:continue print(jack_g.send(food))
执行
producer()
运行结果
jack 说:我开动啦 >>: 骨头 jack eat 骨头 ['骨头'] >>: 菜汤 jack eat 菜汤 ['骨头', '菜汤'] >>: 包子 jack eat 包子 ['骨头', '菜汤', '包子']
1.4 解决初始化问题
利用装饰器,解决迭代器的初始化问题
# 装饰器,初始化yield形成的迭代器 def init(func): def wrapper(*args, **kwargs): g = func(*args, **kwargs) next(g) return g return wrapper @init # 初始化eater的next方法的初始化 def eater(name): print('%s 说:我开动啦' %name) food_list = [] while True: food = yield food_list food_list.append(food) #['骨头','菜汤'] print('%s eat %s' %(name,food)) jack_g = eater('jack') print(jack_g.send('骨头')) print(jack_g.send('菜汤'))
2. 面向过程
面向过程:核心是过程二字,过程即解决问题的步骤,基于面向过程去设计程序就像是在设计一条工业流水线,是一种机械式的思维方式
优点:程序结构清晰,可以把复杂的问题简单化,流程化
缺点:可扩展性差,一条流线只是用来解决一个问题
应用场景:linux内核,git,httpd,shell脚本
示例:模拟linux命令grep,进行过滤,打印符合条件的文件名
#grep -rl "error" /dir/ 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: for file in files: abspath = r"%s\%s" %(pardir,file) target.send(abspath) # 第二阶段:打开文件 @init def opener(target): while True: abspath = yield with open(abspath,"r",encoding="utf-8") as read_f: target.send((abspath,read_f)) # 第三阶段:循环读出每一行内容 @init def cat(target): while True: abspath,file = yield for line in file: tag = target.send((abspath,line)) if tag: break # 第四阶段:过滤 @init def grep(target,pattern): tag = False while True: abspath,line = yield tag tag = False if pattern in line: tag = True target.send(abspath) # 第五阶段:打印该行属于的文件名 @init def printer(): while True: abspath = yield print(abspath) g = search(opener(cat(grep(printer(),"error")))) g.send(r"D:pythoncodeLearningday05a")
3. 递归
3.1 介绍
递归:即递归调用,函数在调用时,直接或间接调用了自身。
递归的执行分为两个阶段:
1.递推
2.回溯
递归的特点:
1.必须有一个明确的结束条件
2.每次进入更深一层时,问题规模相比上次递归都应有所减少
3.递归效率不高,递归层次过多会导致栈溢出
3.2 直接调用
def func(): print("from in func.") func() func()
3.3 间接调用
def foo(): print("from foo.") bar() def bar(): print("from bar.") foo() foo()
3.4 示例
# age(5) = age(4) + 2 # age(4) = age(3) + 2 # age(3) = age(2) + 2 # age(2) = age(1) + 2 # age(1) = 18 # 也就是 # age(n) = age(n-1) + 2 # age(1) = 18 def age(n): if n == 1: return 18 return age(n-1)+2 print(age(5))
循环打印列表的内容
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)
3.5 二分查找
l = [1,2,5,7,10,31,44,47,56,99,102,130,240] def binary_search(l,num): if len(l) == 1: if num == l[0]: print("find it") else: print("not exist") return mid_index = len(l)//2 if num > l[mid_index]: # in the right l = l[mid_index+1:] binary_search(l,num) elif num < l[mid_index]: # in the left l = l[:mid_index] binary_search(l,num) else: print("find it") binary_search(l,99)
更加实用版本二分查找
l = [1,2,5,7,10,31,44,47,56,99,102,130,240] def binart_search(l,num): # 判断l是否为列表 if not isinstance(l,list): print("l is not list") return # 判断列表是否为空 if len(l) == 0: print("not exists") return # 判断列表是否只有一个值 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_value: l = l[:mid_index] binart_search(l,num) binart_search([],40)
5. 模块
5.1 导入模块干了哪些事
1.执行源文件
2.以一个源文件的全局名称空间
3.在当前位置拿到一个模块名,指向2创建的名称空间
5.1 import导入模块
示例:在test_import.py中导入spam.py
# 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 # test_import.py import spam spam.read1() spam.read2() spam.change() spam.read2()
运行结果
from the spam.py spam->read1->money 1000 spam->read2 calling read spam->read1->money 1000 spam->read2 calling read spam->read1->money 0
利用import导入,可以利用as设置别名
示例:模拟sql模块
#mysql.py def sqlparse(): print('mysql sqlparse') #oracle.py def sqlparse(): print('orale sqlparse')
在sql_test.py中进行测试
sql_type = input("sql_type>>:") if sql_type == "mysql": import mysql as sql if sql_type == "oracle": import oracle as sql sql.sqlparse()
5.3 from…import导入模块
优点:使用源文件内的名字时无需加前缀,使用方便
缺点:容易与当前文件的名称空间的名字混淆
示例
from spam import money print(money) # 使用spam模块变量money
导入spam模块的所有内容,需要利用*进行导入模块
from spam import *
这种方法需要慎重使用
5.4 查看模块加载到内存
模块只有在第一次导入时才会执行,之后的导入都是直接引用内存已经存在的结果;利用sys模块的方法modules进行查看
import sys print("spam" in sys.modules) #存放的是已经加载到内的模块 import spam print("spam" in sys.modules)
5.5 模块的搜索路径
模块搜索,优先从内存中查找,然后从内置模块中查找,最后从当前目录中查找。
结论:
1.自定义的模块名一定不要与python自带的模块名重名
2.模块搜索路径:内存中-->内置模块路径-->sys.path
sys.path路径的初始化:
1.执行文件所在的当前路径
2.PYTHONPATH
3.依赖安装时默认指定的
5.6 区分模块的两种用法
模块的两种用法:
1.文件当做脚本运行时__name__等价于__main__
2.文件当做模块被加载运行时__name__等于模块名
# m1.py def func1(): print('from m1') def func2(): print('from m2') def func3(): print('from m3') if __name__ == '__main__': #当做脚本使用 func1() func2() func3() # run.py import m1 m1.func1()
7. 包
7.1 说明
1.无论是import形式还是from…import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法。点的左边是包
2.包的目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)
3.import导入文件时,产生名称空间中的名字来源于文件,import包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件。
强调:
1.在python3中,即使包下没有__init__.py文件,import包仍然不会报错,而在python2中,包下一定要有该文件,否则import包报错
2.创建包的目的不是为了运行,而是被导入使用,记住:包只是模块的一种形式而已,包即模块。
7.2 绝对导入与相对导入
glance/ #Top-level package ├── __init__.py #Initialize the glance package ├── api #Subpackage for api │ ├── __init__.py │ ├── versions.py ├── cmd #Subpackage for cmd ├── __init__.py └── manage.py #文件内容 #policy.py def get(): print('from policy.py') #manage.py def main(): print('from manage.py')
绝对导入:以glance作为起始
相对导入:用.或者..的方式作为起始
# 绝对导入,以glance作为起始 from glance.api.policy import get from glance.cmd.manage import main # 相对导入,以.或者..作为起始 from .api.policy import get from .cmd.manage import main
8. 软件开发规范
#=============>bin目录:存放执行脚本 #start.py import sys,os BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(BASE_DIR) from core import core from conf import my_log_settings if __name__ == '__main__': my_log_settings.load_my_logging_cfg() core.run() #=============>conf目录:存放配置文件 #config.ini [DEFAULT] user_timeout = 1000 [egon] password = 123 money = 10000000 [alex] password = alex3714 money=10000000000 [yuanhao] password = ysb123 money=10 #settings.py import os config_path=r'%s\%s' %(os.path.dirname(os.path.abspath(__file__)),'config.ini') user_timeout=10 user_db_path=r'%s\%s' %(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'db') #my_log_settings.py """ logging配置 """ import os import logging.config # 定义三种日志输出格式 开始 standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' '[%(levelname)s][%(message)s]' #其中name为getlogger指定的名字 simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s' # 定义日志输出格式 结束 logfile_dir = r'%slog' %os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # log文件的目录 logfile_name = 'all2.log' # log文件名 # 如果不存在定义的日志目录就创建一个 if not os.path.isdir(logfile_dir): os.mkdir(logfile_dir) # log文件的全路径 logfile_path = os.path.join(logfile_dir, logfile_name) # log配置字典 LOGGING_DIC = { 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'standard': { 'format': standard_format }, 'simple': { 'format': simple_format }, }, 'filters': {}, 'handlers': { #打印到终端的日志 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', # 打印到屏幕 'formatter': 'simple' }, #打印到文件的日志,收集info及以上的日志 'default': { 'level': 'DEBUG', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件 'formatter': 'standard', 'filename': logfile_path, # 日志文件 'maxBytes': 1024*1024*5, # 日志大小 5M 'backupCount': 5, 'encoding': 'utf-8', # 日志文件的编码,再也不用担心中文log乱码了 }, }, 'loggers': { #logging.getLogger(__name__)拿到的logger配置 '': { 'handlers': ['default', 'console'], # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕 'level': 'DEBUG', 'propagate': True, # 向上(更高level的logger)传递 }, }, } def load_my_logging_cfg(): logging.config.dictConfig(LOGGING_DIC) # 导入上面定义的logging配置 logger = logging.getLogger(__name__) # 生成一个log实例 logger.info('It works!') # 记录该文件的运行状态 if __name__ == '__main__': load_my_logging_cfg() #=============>core目录:存放核心逻辑 #core.py import logging import time from conf import settings from lib import read_ini config=read_ini.read(settings.config_path) logger=logging.getLogger(__name__) current_user={'user':None,'login_time':None,'timeout':int(settings.user_timeout)} def auth(func): def wrapper(*args,**kwargs): if current_user['user']: interval=time.time()-current_user['login_time'] if interval < current_user['timeout']: return func(*args,**kwargs) name = input('name>>: ') password = input('password>>: ') if config.has_section(name): if password == config.get(name,'password'): logger.info('登录成功') current_user['user']=name current_user['login_time']=time.time() return func(*args,**kwargs) else: logger.error('用户名不存在') return wrapper @auth def buy(): print('buy...') @auth def run(): print(''' 购物 查看余额 转账 ''') while True: choice = input('>>: ').strip() if not choice:continue if choice == '1': buy() if __name__ == '__main__': run() #=============>db目录:存放数据库文件 #alex_json #egon_json #=============>lib目录:存放自定义的模块与包 #read_ini.py import configparser def read(config_file): config=configparser.ConfigParser() config.read(config_file) return config
9. 日志模块
import logging formatter1 = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s", datefmt="[%Y-%m-%d %H:%M:%S %p]",) fh1 = logging.FileHandler("test1.log") fh2 = logging.FileHandler("test2.log") ch = logging.StreamHandler() fh1.setFormatter(formatter1) fh2.setFormatter(formatter1) ch.setFormatter(formatter1) logger1 = logging.getLogger("jack") logger1.setLevel(10) logger1.addHandler(fh1) logger1.addHandler(fh2) logger1.addHandler(ch) logger1.debug("debug") logger1.info("info") logger1.error("error")