协程函数、面向过程编程、模块与包

一、协程函数

1 协程函数的定义

协程函数就是使用了yield表达式形成的生成器

实例:

def eater(name):
    print('%s eat food' %name)
    while True:
        food = yield
    print('done')

g = eater('Yim')
print(g)

#执行结果:
<generator object eater at 0x000000000227FEB8>                  #证明g现在是一个生成器函数

2 协程函数的赋值

第一阶段:next()初始化函数,让函数暂停在yield位置

第二阶段:send()给yield传值

next()和send()都是让函数在上次暂停的位置继续执行。next是让函数初始化,send会给yield赋值并触发下一次代码执行

实例:

def eater(name):
    print('%s start to eat food' %name)
    while True:
        food = yield
        print('%s eat %s' %(name,food))

e = eater('Yim')

next(e)                             #初始化,等同于e.send(None)
e.send('米饭')                      #给yield传值
e.send('大饼')

#执行结果
Yim start to eat food
Yim eat 米饭
Yim eat 大饼
def eater(name):
    print('%s start to eat food' %name)
    food_list = []
    while True:
        food = yield food_list
        food_list.append(food)
        print('%s eat %s' %(name,food))

e = eater('Yim')

next(e)                  #初始化
print(e.send('米饭'))          #1、给yield传值   2、继续往下执行,直到再次碰到yield,然后暂停并且把yield后的返回值当做本次调用的返回值
print(e.send('大饼'))

#执行结果
Yim start to eat food
Yim eat 米饭
['米饭']
Yim eat 大饼
['米饭', '大饼']

3 用装饰器初始化协程函数

def init(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        next(res)
        return res
    return wrapper

@init
def eater(name):
    print('%s start to eat food' %name)
    food_list = []
    while True:
        food = yield food_list
        food_list.append(food)
        print('%s eat %s' %(name,food))

e = eater('Yim')
print(e.send('米饭'))
print(e.send('大饼'))

 

二、 面向过程编程

面向过程的编程思想:流水线式的编程思想,在设计程序时,需要把整个流程设计出来

优点:

  体系结构更清晰

  简化程序的复杂度

缺点:

  可扩展性差

应用场景:

  Linux内核、git、httpd、shell脚本

实例:

# grep -rl 'error' /dir

import os

#初始化装饰器
def init(func):
    def wrapper(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return wrapper

#1、找到所有文件的绝对路径 --os.walk
@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)

#2、打开文件 --open
@init
def opener(target):
    while True:
        abspath=yield                           #  接收search传递的路径
        with open(abspath,'rb') as f:
            target.send((abspath,f))            # send多个用元组的方式,为了把文件的路径传递下去

#3、循环读取每一行内容 --for line in f
@init
def cat(target):
    while True:
        abspath,f=yield #(abspath,f)
        for line in f:
            res=target.send((abspath,line))     # 同时传递文件路径和每一行的内容
            if res:break

#4、过滤关键字 --if 'error' in line
@init
def grep(pattern,target):           # # patter是过滤的参数
    tag=False                       #为了去掉重复的文件路径,因为一个文件中存在多个error关键字
    while True:
        abspath,line=yield tag
        tag=False
        if pattern in line:
            target.send(abspath)    # 传递有相应内容的文件路径
            tag=True

#5、打印该行属于的文件名
@init
def printer():
    while True:
        abspath=yield
        print(abspath)

g = search(opener(cat(grep('os'.encode('utf-8'), printer()))))
g.send(r'F:\Python\Code')

 

三、函数的递归调用

在调用一个函数的过程中,直接或间接的调用了函数本身,就叫递归调用

执行有两个阶段:递推和回溯

递归的效率低,需要在进入下一次递归时保留当前的状态,解决方法是尾递归,即在函数的最后一步(而非最后一行)调用自己,但是python又没有尾递归,且对递归层级做了限制

1. 必须有一个明确的结束条件

2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

#直接
def func():
    print('from func')
    func()

func()

 
#间接
def foo():
    print('from foo')
    bar()

def bar():
    print('from bar')
    foo()

foo()
# salary(5)=salary(4)+300
# salary(4)=salary(3)+300
# salary(3)=salary(2)+300
# salary(2)=salary(1)+300
# salary(1)=100
#
# salary(n)=salary(n-1)+300     n>1
# salary(1) =100                       n=1

def salary(n):
    if n == 1:
        return 100
    return salary(n-1)+300

print(salary(5))    

 

四、模块

如果你从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。为此 Python 提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。

模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是Python标准库的方法

1 import语句

import加载的模块分为四个通用类别:

  1. 使用Python编写的代码(.py文件)
  2. 已被编译为共享库或DLL的C或C++扩展
  3. 包好一组模块的包
  4. 使用C编写并链接到Python解释器的内置模块

import语法:

       import module1[, module2[,... moduleN]

当解释器遇到import语句,如果模块在当前的搜索路径就会被导入,搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块 support,需要把命令放在脚本的顶端:

support.py 文件代码为:

def print_func( par ):
    print ("Hello : ", par)
  return

test.py引入support模块:

import support              #导入模块

support.print_func('Yim')   #现在可以调用模块里包含的函数了

#执行结果
Hello :  Yim

还可以给模块起一个别名:

import support as s1

s1.print_func('Yim')

一个模块只会被导入一次,不管你执行了多少次import。这样可以防止导入模块被一遍又一遍地执行(可以使用sys.modules查看验证):

import support
import support
import support

support.print_func('Yim')

#执行结果:
Hello :  Yim

2 from…import*语句

优点:使用源文件内的名字时无需加前缀,使用方便

缺点:容易与当前文件的名称空间内的名字混淆

把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

from modname import *               #不推荐使用

下面只导入support模块里面的print_func函数:

from support import print_func      #可以用逗号分隔,写上多个函数名

print_func('Yim')                   #直接执行print_func函数

也可以起别名:

from support import print_func as p1

p1('Yim')

可以使用__all__来控制*,修改support.py文件:

__all__ = [print_func]                  #可以在列表内添加多个函数名

def print_func( par ):
    print ("Hello : ", par)
    return

test.py文件如下:

from support import *

print_func('Yim')                       #执行结果报错,NameError: name 'print_func' is not defined

3 模块的搜索路径

搜索路径是由一系列目录名组成的,Python解释器就依次从这些目录中去寻找所引入的模块

搜索路径是在Python编译或安装的时候确定的,安装新的库应该也会修改。搜索路径被存储在sys模块中的path变量

模块的查找顺序是:内存中已经加载的模块->内置模块->sys.path路径中包含的模块

需要特别注意的是:我们自定义的模块名不应该与系统内置模块重名

import sys

print(sys.path)

#执行结果:
['F:\\Python\\Code\\模块', 'F:\\Python', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages']

在脚本中修改sys.path,引入一些不在搜索路径中的模块

import sys

print(sys.path)

sys.path.append('F:\\Python\\Code\\不常用模块')
# sys.path.insert(0,'F:\\Python\\Code\\不常用模块')        #也可以使用insert,排在前的目录会优先被搜索

print(sys.path)

#执行结果
['F:\\Python\\Code\\模块', 'F:\\Python\\2017-18s', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages']
['F:\\Python\\Code\\模块', 'F:\\Python\\2017-18s', 'C:\\Python36\\python36.zip', 'C:\\Python36\\DLLs', 'C:\\Python36\\lib', 'C:\\Python36', 'C:\\Python36\\lib\\site-packages', 'F:\\Python\\Code\\不常用模块']

以下是官方文档:

#官网链接:https://docs.python.org/3/tutorial/modules.html#the-module-search-path
搜索路径:
当一个命名为spam的模块被导入时
    解释器首先会从内建模块中寻找该名字
    找不到,则去sys.path中找该名字

sys.path从以下位置初始化
    1 执行文件所在的当前目录
    2 PYTHONPATH(包含一系列目录名,与shell变量PATH语法一样)
    3 依赖安装时默认指定的

注意:在支持软连接的文件系统中,执行脚本所在的目录是在软连接之后被计算的,换句话说,包含软连接的目录不会被添加到模块的搜索路径中
在初始化后,我们也可以在python程序中修改sys.path,执行文件所在的路径默认是sys.path的第一个目录,在所有标准库路径的前面。这意味着,当前目录是优先于标准库目录的,需要强调的是:我们自定义的模块名不要跟python标准库的模块名重复,除非你是故意的

4 __name__属性

一个模块被另一个程序第一次引入时,其主程序将运行。如果我们想在模块被引入时,模块中的某一程序块不执行,我们可以用__name__属性来使该程序块仅在该模块自身运行时执行。

if __name__ == '__main__':                      #文件当做脚本运行时,__name__ 等于'__main__'
   print('程序自身在运行')
else:
   print('我来自另一模块')

#执行结果
程序自身在运行 
#交互式,导入模块
>>> import support
我来自另一模块

说明: 每个模块都有一个__name__属性,当其值是'__main__'时,表明该模块自身在运行,否则是被引入。

常用实例:

def func1():
    print('from func1')

def func2():
    print('from func2')

if __name__ == '__main__':
   func1()
   func2()

 

五、包

包是一种管理 Python 模块命名空间的形式,采用"点模块名称",比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B

无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,这都是关于包才有的导入语法

包是目录级的(文件夹级),文件夹是用来组成py文件(包的本质就是一个包含__init__.py文件的目录)

强调:

  1. 在python3中,即使包下没有__init__.py文件,import 包仍然不会报错,而在python2中,包下一定要有该文件,否则import 包报错
  2. 创建包的目的不是为了运行,而是被导入使用,记住,包只是模块的一种形式而已,包即模块

有一个包结构如下:

glance/                     #顶层包
├── __init__.py        #初始化 glance 包
├── api
│   ├── __init__.py
│   ├── policy.py

导入包里的子模块:

import glance.api.policy

必须使用全名去访问:

glance.api.policy.func()

还有一种导入模块的方法是:

from glance.api import policy               #从模块中导入一个指定的部分到当前命名空间中

它不需要那些冗长的前缀,所以可以这样使用:

policy.func()

还有一种就是直接导入一个函数或者变量:

from glance.api.policy import func

可以直接使用func()函数:

func()

1 导入包实例

包结构如下:

glance/                     #顶层包
run.py                      # 执行文件
├── __init__.py        #初始化 glance 包
├── api
│   ├── __init__.py
│   ├── policy.py     

导入包:

#1、run.py用全名访问func(),内容如下:
import glance
glance.api.policy.func()
# glance/__init__.py如下:
import glance.api                           #也可以用from glance import api
# glance/api/__init__.py如下:
import glance.api.policy                    #也可以用from glance.api import policy

#2、run.py直接访问func(),内容如下:
import glance
glance.func()
# glance/__init__.py如下:
from glance.api.policy import func          #不能用import glance.api.policy.func
# glance/api/__init__.py可以不用写

当使用from package import item这种形式的时候,对应的item既可以是包里面的子模块(子包),或者包里面定义的其他名称,比如函数,类或者变量。

import语法会首先把item当作一个包定义的名称,如果没找到,再试图按照一个模块去导入。如果还没找到,恭喜,一个:exc:ImportError 异常被抛出了。

反之,如果使用形如import item.subitem.subsubitem这种导入形式,除了最后一项,都必须是包,而最后一项则可以是模块或者是包,但是不可以是类,函数或者变量的名字。

注意事项:

  1. 关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则
  2. 对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)
  3. 对比import item 和from item import name的应用场景:如果我们想直接使用name那必须使用后者。

2 绝对导入和相对导入

以上面的例子是绝对导入的方式

绝对导入:以glance作为起始

相对导入:用.或者..的方式作为起始(只能在一个包中使用,不能用于不同目录内)

实例:

run.py直接访问func(),内容如下:
import glance
glance.func()
# glance/__init__.py如下:
from .api.policy import func        #1、这样的好处是:就算包名glance被更改也不会有影响,只需改run.py内的import …2、..表示上级目录,可以导入上级目录中的其他模块

特别需要注意的是:

  • 可以用import导入内置或者第三方模块(已经在sys.path中),但是要绝对避免使用import来导入自定义包的子模块(没有在sys.path中),应该使用from... import ...的绝对或者相对导入
  • 包的相对导入只能用from的形式

 

六、软件开发规范

 

bin:存放执行脚本
conf:存放配置文件
core:存放核心逻辑
db:存放数据库文件
lib:存放自定义的模块与包
log:存放日志

start.py内容:

import os
import sys

# print(os.path.abspath(__file__))                       #获取当前文件的绝对路径,F:\soft\bin\test.py
# print(os.path.dirname(os.path.abspath(__file__)))       #获取当前文件所在的目录,F:\soft\bin
# print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))      #获取当前文件所在的目录的上级目录,F:\soft

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)                                   #将soft这个包的路径加到环境变量

from core import core
    ……                      #导入其他模块

if if __name__ == '__main__':
    ……                      #执行

 

posted on 2017-08-02 19:36 似水_流年 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/yanmj/p/7275650.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值