zoukankan      html  css  js  c++  java
  • 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")
  • 相关阅读:
    MySQL left join 查询很慢(1)
    Windows 搭建 hbase 单机版
    Spark SQL 编程(七)
    Spark RDD 操作(三)
    并发编程(一)
    并发编程之 multiprocessing 和 concurrent.futures(二)
    Spark 系列之环境搭建(一)
    Python3之并发(七)---线程同步队列(queue)
    Python3之并发(六)---线程池
    Python3之并发(五)---线程条件(Condition)和事件(Event)
  • 原文地址:https://www.cnblogs.com/goodshipeng/p/7282047.html
Copyright © 2011-2022 走看看