python基础-协程函数、递归、模块、包等内容

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
View Code

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")
原文地址:https://www.cnblogs.com/goodshipeng/p/7282047.html