pyday05

一、生成器(yield用法)

 1.yield两种用法:

  yield的语句形式:

def foo():
    print('first')
    yield 1
    print('second')
    yield 2
    print('third')
    yield 3
    print('fourth')
    yield 4
    print('fifth')

g=foo() # 直接加()是不会触发执行迭代器函数的,而是获得生成器对象

print(g)
for i in g:
    print(i)

运行结果:
<generator object foo at 0x00000000026C2630>
first
second
third
fourth
fifth
View Code
print(next(g))    #使用next()方法触发迭代器g的执行,进而触发函数的执行
print(next(g))
print(next(g))
print(next(g))
print(next(g))

next()方法碰到yield关键字会停止执行并挂起,直到下次使用next(),下次开始执行的地方就是挂起的地方。  

  yield的表达式用法:

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

g = eater('dylan')

next(g)
next(g)

运行结果:
dylan ready to eat    #第一次next()
dylan eat None    #第二次next(),由于food没有定义,所以为None

    可以使用dec.send(var)方法给yield传参数,也同时相当于对生成器对象使用next():

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

g = eater('dylan')

next(g)
g.send('food1')
g.send('food2')

运行结果:
dylan ready to eat
dylan eat food1
dylan eat food2

    表达式形式的yield开始时必须先传一个None值[send(None)或next()都可以]进行初始化到达初始化位置,否则报错:

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

g = eater('dylan')

# next(g)
# g.send(None)
g.send('food1')
g.send('food2')

运行结果:
Traceback (most recent call last):
  File "D:/python/py17/code/py17day05/lesson/yield_test01.py", line 20, in <module>
    g.send('food1')
TypeError: can't send non-None value to a just-started generator

    可以定义一个装饰器对yield进行初始化:

def send_none(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        next(res)    #在装饰器里执行原函数的next()
        return res
    return wrapper

@send_none    # eater = send_none(eater)
def eater(name):
    print('%s ready to eat'%name)
    while True:
        food = yield
        print('%s eat %s'%(name,food))

g = eater('dylan')

# next(g)
g.send('food1')
g.send('food2')

运行结果:
dylan ready to eat
dylan eat food1
dylan eat food2
View Code

    也可以利用yield进行值的返回:

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

@send_none    # eater = send_none(eater)
def eater(name):
    food_list = []
    print('%s ready to eat'%name)
    while True:
        food = yield
        print('%s eat %s'%(name,food))

g = eater('dylan')

# next(g)
print(g.send('food1'))
print(g.send('food2'))

运行结果:
dylan ready to eat
dylan eat food1
None    #没有在yield后定义返回结果,所以返回为None        
dylan eat food2
None
yield后无返回值
def send_none(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        next(res)
        return res
    return wrapper

@send_none    # eater = send_none(eater)
def eater(name):
    food_list = []    #定义一个空列表
    print('%s ready to eat'%name)
    while True:
        food = yield food_list    # 返回这个列表
        food_list.append(food)    # 将send的值加入列表
        print('%s eat %s'%(name,food))

g = eater('dylan')

# next(g)
print(g.send('food1'))
print(g.send('food2'))

运行结果:
dylan ready to eat
dylan eat food1
['food1']
dylan eat food2
['food1', 'food2']
表达式形式yield有返回值

 2.表达式形式yield应用:

  实现模拟gred -rl 'python' /root:

  目录结构:

      

  关于os.walk:

import os

g = os.walk(r'D:\python\py17\code\py17day05\a')

for i in g:
    print(i)

运行结果:
('D:\\python\\py17\\code\\py17day05\\a', ['b'], ['a', 'a1'])
('D:\\python\\py17\\code\\py17day05\\a\\b', ['c'], ['b', 'b1'])
('D:\\python\\py17\\code\\py17day05\\a\\b\\c', ['d'], ['c', 'c1'])
('D:\\python\\py17\\code\\py17day05\\a\\b\\c\\d', [], ['d', 'd1'])

'''
遍历结果表示了每一层级文件夹的详情,
第一个元素为文件夹路径,
第二个元素为当前路径包含的子文件夹,
第三个元素为当前路径下的文件
'''
os.walk
import os

def get_abs_path(walk_path):
    w = os.walk(walk_path)
    for path,_,files in w:
        for file in files:
            file_abs_path = r'%s\%s' % (path, file)
            print(file_abs_path)
p = r'D:\python\py17\code\py17day05\a'
get_abs_path(p)

运行结果:
D:\python\py17\code\py17day05\a\a
D:\python\py17\code\py17day05\a\a1
D:\python\py17\code\py17day05\a\b\b
D:\python\py17\code\py17day05\a\b\b1
D:\python\py17\code\py17day05\a\b\c\c
D:\python\py17\code\py17day05\a\b\c\c1
D:\python\py17\code\py17day05\a\b\c\d\d
D:\python\py17\code\py17day05\a\b\c\d\d1
View Code

  对于函数调用方式来说,根据检索的路径不同,每次都要重新调用一次get_abs_path函数,

使用yield表达式用法可以实现“一次调用,多次赋值”:

import os

def send_none(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        res.send(None)    # next(res)
        return res
    return wrapper

@send_none
def get_abs_path():
    while True:
        walk_path = yield
        w = os.walk(walk_path)
        for path,_,files in w:
            for file in files:
                file_abs_path = r'%s\%s' % (path, file)
                print(file_abs_path)

p = r'D:\python\py17\code\py17day05\a'
g = get_abs_path()
g.send(p)
g.send(r'D:\python\py17\code\py17day05\lesson')

运行结果:
D:\python\py17\code\py17day05\a\a
D:\python\py17\code\py17day05\a\a1
D:\python\py17\code\py17day05\a\b\b
D:\python\py17\code\py17day05\a\b\b1
D:\python\py17\code\py17day05\a\b\c\c
D:\python\py17\code\py17day05\a\b\c\c1
D:\python\py17\code\py17day05\a\b\c\d\d
D:\python\py17\code\py17day05\a\b\c\d\d1
D:\python\py17\code\py17day05\lesson\yield_test01.py
D:\python\py17\code\py17day05\lesson\yield表达式用法.py
D:\python\py17\code\py17day05\lesson\__init__.py
View Code

  初步思路,因为已经实现获得目录下每一个文件和其子文件夹文件的绝对路径,接下来要将这些文件做打开操作,打开操作需要将绝对路径传入打开函数中,我们也通过send进行传值:

import os

def send_none(func):
    def wrapper(*args,**kwargs):
        res = func(*args,**kwargs)
        res.send(None)    # next(res)
        return res
    return wrapper

@send_none
def get_abs_path(open_file):
    while True:
        walk_path = yield
        w = os.walk(walk_path)
        for path,_,files in w:
            for file in files:
                file_abs_path = r'%s\%s' % (path, file)
                # print(file_abs_path)
                open_file.send(file_abs_path)    # 将得到的绝对路径通过send方式传给open_file这个生成器函数
@send_none
def open_file():
    while True:
        file_abs_path = yield
        with open(file_abs_path,encoding='utf-8') as f:
            print(f)

p = r'D:\python\py17\code\py17day05\a'
g = get_abs_path(open_file())    # get_abs_path本身也是生成器函数,要通过send方法调用
g.send(p)

运行结果:
<_io.TextIOWrapper name='D:\\python\\py17\\code\\py17day05\\a\\a' mode='r' encoding='utf-8'>
<_io.TextIOWrapper name='D:\\python\\py17\\code\\py17day05\\a\\a1' mode='r' encoding='utf-8'>
<_io.TextIOWrapper name='D:\\python\\py17\\code\\py17day05\\a\\b\\b' mode='r' encoding='utf-8'>
<_io.TextIOWrapper name='D:\\python\\py17\\code\\py17day05\\a\\b\\b1' mode='r' encoding='utf-8'>
<_io.TextIOWrapper name='D:\\python\\py17\\code\\py17day05\\a\\b\\c\\c' mode='r' encoding='utf-8'>
<_io.TextIOWrapper name='D:\\python\\py17\\code\\py17day05\\a\\b\\c\\c1' mode='r' encoding='utf-8'>
<_io.TextIOWrapper name='D:\\python\\py17\\code\\py17day05\\a\\b\\c\\d\\d' mode='r' encoding='utf-8'>
<_io.TextIOWrapper name='D:\\python\\py17\\code\\py17day05\\a\\b\\c\\d\\d1' mode='r' encoding='utf-8'>

   接下来打开文件后,要遍历文件里的每一行,找到关键字是否存在于改行,存在关键字的文件,打印输出,传递参数同样使用send():

 1 import os
 2 
 3 def send_none(func):
 4     '''
 5     装饰器函数:初始化表达式yield的生成器函数(send一个None)
 6     '''
 7     def wrapper(*args,**kwargs):
 8         res = func(*args,**kwargs)
 9         res.send(None)    # next(res)
10         return res
11     return wrapper
12 
13 @send_none
14 def get_abs_path(open_file):
15     '''
16     获取目录下所有文件的绝对路径函数,并send给open_file 
17     '''
18     while True:
19         walk_path = yield
20         w = os.walk(walk_path)
21         for path,_,files in w:
22             for file in files:
23                 file_abs_path = r'%s\%s' % (path, file)
24                 # print(file_abs_path)
25                 open_file.send(file_abs_path)    # 将得到的绝对路径通过send方式传给open_file这个生成器函数
26 
27 @send_none
28 def open_file(match_line):
29     '''
30     获得文件句柄函数,并将文件句柄和文件路径以元祖的形式send给match_line
31     '''
32     while True:
33         file_abs_path = yield
34         with open(file_abs_path,encoding='utf-8') as f:
35             # print(f)
36             match_line.send((file_abs_path,f))    # send可以将多个参数打包成元祖传递给生成器函数
37 
38 @send_none
39 def match_line(word):
40     '''
41     遍历通过yield获取到的文件里的每一行,找到关键字是否存在于改行,存在关键字的文件,打印输出
42     (使用标签位位,避免重复打印)
43     '''
44     while True:
45         file_abs_path,f = yield
46         Flag = False    # 定义一个标志位
47         for line in f:
48             if Flag:    # 如果标志位被更改为True则跳出循环(为了防止一个文件有多行关键字,print时存在重复)
49                 break
50             if word in line :
51                 Flag = True    # 当匹配到关键字所在的行,将标志位改为Ture
52                 print(file_abs_path)
53 
54 walk_path = r'D:\python\py17\code\py17day05\a'
55 g = get_abs_path(open_file(match_line('python')))    # 获得get_abs_path()生成器对象
56 g.send(walk_path)
grep -rl 'python' /
运行结果:
D:\python\py17\code\py17day05\a\a1
D:\python\py17\code\py17day05\a\b\c\c1
D:\python\py17\code\py17day05\a\b\c\d\d1

 

二、面向过程编程思想

   面向过程的程序设计:是一种流水线式的编程思路,是机械式的

    优点:程序的结构清晰,可以把复杂的问题简单

    缺点:1 扩展性差

三、匿名函数,max,min,sorted,zip,map,reduce,filter

 1.匿名函数lambda:

  一般来说定义一个函数

def func(x,y,z=1):
    return x+y+z

  匿名函数为:

lambda x,y,z=1:x+y+z

  匿名就是没有名字,与函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放

  (*引用计数:当一个值引用计数为0时,会被回收)

2.max,min,sorted,zip,map,reduce,filter结合lambda使用:

  如果要获取一个字典value的最大值所对应的key,可以使用zip

salaries={
'网管':7000,
'CTO':100000000,
'技术总监':50000,
'程序员':25000
}

res = zip(salaries.values(),salaries.keys())
print(max(res))

运行结果:
(100000000, 'CTO')

  使用lambda结合max实现:

    这里key是一个函数,用来选取参与比较的元素,

salaries={
'网管':7000,
'CTO':100000000,
'技术总监':50000,
'程序员':25000
}

print(max(salaries,key=lambda x:salaries[x]))

运行结果:
CTO

  min同上

  sorted结合lambda进行字典排序:

salaries={
'网管':7000,
'CTO':100000000,
'技术总监':50000,
'程序员':25000
}

print(sorted(salaries,key=lambda x:salaries[x]))    # 默认从小到大
print(sorted(salaries,key=lambda x:salaries[x],reverse=True))    # 从大到小

运行结果:
['网管', '程序员', '技术总监', 'CTO']
['CTO', '技术总监', '程序员', '网管']

  map(func,iter1,iter2)用法:

  对iter中的元素依次执行func(item),见执行结果组成一个List返回:

res = map(lambda x:x*x*x,range(1,11))
print(res)
print(list(res))

运行结果:
<map object at 0x000000000105C1D0>
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

  另外map也支持多个iter,这就要求func也支持相应数量的参数输入:

res = map(lambda x,y:x+y,range(1,11),range(1,11))
print(list(res))

运行结果:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

 

  reduce(function, sequence, starting_value)

  对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用,例如可以用来对List求和:

from functools import reduce

res = reduce(lambda x,y:x+y,range(1,11))
res1 = reduce(lambda x,y:x+y,range(1,11),20)

print(res)
print(res1)

运行结果:
55 #(注:1+2+3+4+5+6+7+8+9+10)
75 #(注:1+2+3+4+5+6+7+8+9+10+20)

  filter(function, iterable):

  对iterable中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于iterable的类型)返回:

res = filter(lambda x:x%2==0,range(10))
print(res)
print(list(res))

运行结果:
<filter object at 0x000000000073C1D0>
[0, 2, 4, 6, 8]

  

四、递归与二分法

 1.递归调用:

  在函数调用过程中,直接或者间接调用了函数本身

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

func1()

运行结果:
RecursionError: maximum recursion depth exceeded while calling a Python object

  运行结果显示并不会无限的递归,使用sys.getrecursionlimit()可以得到默认层数:

import sys
print(sys.getrecursionlimit())

运行结果:
1000    # 默认递归层数为1000

# sys.setrecursionlimit()可以设置这个值
def age(n):
    if n == 1:
        return 18
    return age(n-1) + 2

print(age(5))

运行结果:
26
age

  需要注意的几点:

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

    2.每次递归,问题的规模要减少

 

 

2.二分法实现查找:

l = [1, 2, 10,33,53,71,73,75,77,85,101,201,202,999,11111]

def match_num(num,seq):
    if seq == []:    # 如果列表为空表示所找的值不存在
        print('not exist!')
        return    # 终止递归
    middle_num_index = len(seq)//2
    print(seq,seq[middle_num_index])
    if seq[middle_num_index] == num:
        print('find it!')
    elif seq[middle_num_index] < num:
        seq = seq[middle_num_index + 1:]
        match_num(num,seq)
    elif seq[middle_num_index] > num:
        seq = seq[:middle_num_index]
        match_num(num,seq)

运行结果:
[1, 2, 10, 33, 53, 71, 73, 75, 77, 85, 101, 201, 202, 999, 11111] 75
[1, 2, 10, 33, 53, 71, 73] 33
[1, 2, 10] 2
[10] 10
not exist!

 

五、模块

1.什么是模块:

    一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。

2.为什么使用模块:

    如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。

    随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用,

3.使用模块的两种方法:

  import:

#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
示例文件

  模块可以包含可执行的语句和函数的定义,这些语句的目的是初始化模块,它们只在模块名第一次遇到导入import语句时才执行(import语句是可以在程序中的任意位置使用的,且针对同一个模块很import多次,为了防止你重复导入,python的优化手段是:第一次导入后就将模块名加载到内存了,后续的import语句仅是对已经加载大内存中的模块对象增加了一次引用,不会重新执行模块内的语句),如下 :

#test.py
import spam #只在第一次导入时才执行spam.py内代码,此处的显式效果是只打印一次'from the spam.py',当然其他的顶级代码也都被执行了,只不过没有显示效果.
import spam
import spam
import spam

'''
执行结果:
from the spam.py
'''

  我们可以从sys.module中找到当前已经加载的模块,sys.module是一个字典,内部包含模块名与模块对象的映射,该字典决定了导入模块时是否需要重新导入。

   每个模块都是一个独立的名称空间,定义在这个模块中的函数,把这个模块的名称空间当做全局名称空间,这样我们在编写自己的模块时,就不用担心我们定义在自己模块中全局变量会在被导入时,与使用者的全局变量冲突

#测试一:money与spam.money不冲突
#test.py
import spam 
money=10
print(spam.money)

'''
执行结果:
from the spam.py
1000
'''
测试一:money与spam.money不冲突
#测试二:read1与spam.read1不冲突
#test.py
import spam
def read1():
    print('========')
spam.read1()

'''
执行结果:
from the spam.py
spam->read1->money 1000
'''
测试二:read1与spam.read1不冲突
#测试三:执行spam.change()操作的全局变量money仍然是spam中的
#test.py
import spam
money=1
spam.change()
print(money)

'''
执行结果:
from the spam.py
1
'''
测试三:执行spam.change()操作的全局变量money仍然是spam中的

  import首次导入模块spam时会做三件事:

  1.为源文件(spam模块)创建新的名称空间,在spam中定义的函数和方法若是使用到了global时访问的就是这个名称空间。

    2.以新创建的名称空间为全局名称空间,执行模块中包含的代码(正如import spam时打印了一行)

  3.创建名字spam来引用该命名空间:这个名字和变量名没什么区别,都是‘第一类的’,且使用 'spam.名字' 的方式可以访问spam.py文件中定义的名字,'spam.名字' 与test.py中的名字来自两个完全不同的地方。

  一些补充:

    模块起别名:相当于x=1,y=x 

'''
为已经导入的模块起别名的方式对编写可扩展的代码很有用,假设有两个模块xmlreader.py和csvreader.py,它们都定义了函数read_data(filename):用来从文件中读取一些数据,但采用不同的输入格式。可以编写代码来选择性地挑选读取模块,例如:
'''
if file_format == 'xml':
     import xmlreader as reader
elif file_format == 'csv':
     import csvreader as reader
data=reader.read_date(filename)
示例

 

  from ... import...:

  对比import spam,会将源文件的名称空间'spam'带到当前名称空间中,使用时必须是spam.名字的方式而from 语句相当于import,也会创建新的名称空间,但是将spam中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了

#测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#test.py
from spam import read1
money=10
read1()
'''
执行结果:
from the spam.py
spam->read1->money 1000
'''
测试一:导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
#测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
#test.py
from spam import read2
def read1():
    print('==========')
read2()

'''
执行结果:
from the spam.py
spam->read2 calling read
spam->read1->money 1000
'''
测试二:导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()

  如果当前有重名read1或者read2,那么会有覆盖效果

#测试三:导入的函数read1,被当前位置定义的read1覆盖掉了
#test.py
from spam import read1
def read1():
    print('==========')
read1()
'''
执行结果:
from the spam.py
==========
'''
测试三:导入的函数read1,被当前位置定义的read1覆盖掉了

  需要特别强调的一点是:python中的变量赋值不是一种存储操作,而只是一种绑定关系,如下:

from spam import money,read1
money=100 #将当前位置的名字money绑定到了100
print(money) #打印当前的名字
read1() #读取spam.py中的名字money,仍然为1000

'''
from the spam.py
100
spam->read1->money 1000
'''

  同样也支持as和导入多行:

 from spam import read1 as read
 from spam import read1,read2,money

  关于 from spam import *: 把spam中所有的不是以下划线(_)开头的名字都导入到当前位置,大部分情况下我们的python程序不应该使用这种导入方式,因为*你不知道你导入什么名字,很有可能会覆盖掉你之前已经定义的名字。而且可读性极其的差,在交互式环境中导入时没有问题。

from spam import * #将模块spam中所有的名字都导入到当前名称空间
print(money)
print(read1)
print(read2)
print(change)

'''
执行结果:
from the spam.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>
'''

  可以使用__all__来控制*(用来发布新版本)在spam.py中新增一行:

__all__=['money','read1'] #这样在另外一个文件中用from spam import *就这能导入列表中规定的两个名字

3.__name__:

  __name__为全局变量

  当模块当做脚本执行时:__name__ = __main__

print('from spam.py')

money = 1000

def read1():
    print('spam->read1->money',money)

def read2():
    print('spam->read2->money',money)
    read1()

def change():
    global money
    money = 0

print(__name__)

'''
运行结果:
from spam.py
__main__
'''
View Code

  当模块导入时:__name__ = 模块名

import spam

'''
运行结果:
from spam.py
spam
'''
View Code

  当我们导入模块时,是为了使用模块里的功能,只需要加载模块里名字的定义,而不是立即执行其中的功能,而作为模块的开发者,有时候要直接使用模块做调试,这种需求我们可以通过判断__name__的值来实现:

money = 1000

def read1():
    print('spam->read1->money',money)

def read2():
    print('spam->read2->money',money)
    read1()

def change():
    global money
    money = 0

if __name__ == '__main__':
    print('from spam.py')
    print('This is %s'%__name__)

'''
运行结果
from spam.py
This is __main__
'''
View Code

4. 模块搜索路径:  

  我们执行以下程序:

  运行后,执行完毕第一个import spam后,将目录里的spam.py删除,下一次依然可以正常导入spam模块。

import time
import spam
time.sleep(15)
import spam

运行结果:
from spam.py

  再执行以下程序:

  目录下新建一个sys.py文件,如下:

print('my sys.py')

  之后在同级目录下运行test.py:

import sys
print(sys)

运行结果:
<module 'sys' (built-in)>

  运行结果并没有打印之前由我们自己创建的同级目录下的sys.py。

  以上两个例子可以判断,导入模块的查找路径方式,先在内存中查找是否存在之前导入的模块,然后再去寻找内置模块(如sys,os等),最后在依次查找sys.path所提供的路径(sys.path为一个列表,列表的第一个元素即当前路径,优先查找当前路径)

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

  在第三步查找sys.path时,对于同级目录下的模块,我们可以直接使用import导入,对于import而言,只会查找当前test.py所在目录的相对路径下的内容,如果被导入模块与test.py不在同一级目录(在test.py的上级目录),我们只能将其加入sys.path下才可以使用模块。

  同理from..import也一样,只会查找当前test.py所在目录的相对路径下的内容。

5.补充:

  关于.pyc文件:

  为了提高模块的加载速度,Python缓存编译的版本,每个模块在__pycache__目录的以module.version.pyc的形式命名,通常包含了python的版本号,如在CPython版本3.3,关于spam.py的编译版本将被缓存成__pycache__/spam.cpython-33.pyc,这种命名约定允许不同的版本,不同版本的Python编写模块共存。 

 

  

六、包

 有以下目录结构:

glance/                   #Top-level package

├── __init__.py      #Initialize the glance package

├── api                  #Subpackage for api

│   ├── __init__.py

│   ├── policy.py

│   └── versions.py

├── cmd                #Subpackage for cmd

│   ├── __init__.py

│   └── manage.py

└── db                  #Subpackage for db

    ├── __init__.py

    └── models.py
glance包
 1 #文件内容
 2 
 3 #policy.py
 4 def get():
 5     print('from policy.py')
 6 
 7 #versions.py
 8 def create_resource(conf):
 9     print('from version.py: ',conf)
10 
11 #manage.py
12 def main():
13     print('from manage.py')
14 
15 #models.py
16 def register_models(engine):
17     print('from models.py: ',engine) 
文件内容

 

 

# glance下的 __init__.py :
print('from glance __init__')

# glance同级创建一个test.py:
import glance 

运行结果test.py:
from glance __init__

   我们想在glance同级的test.py里导入glance,结果运行了glance下的__init__.py,可见导入一个包就相当于导入(运行)了__init__.py,同时也产生了一个该__init__.py文件的名称空间。


# api下的 __init__.py :
print('from api __init__')

import
glance glance.api.policy.get() 运行结果: #由于该__init__.py下无api名字,所以报错 from glance __init__ Traceback (most recent call last): File "D:/python/py17/code/py17day05/test.py", line 50, in <module> glance.api.policy.get() AttributeError: module 'glance' has no attribute 'api'

  要想导入执行policy,可以通过如下方式: 

import glance.api.policy
glance.api.policy.get()

运行结果:
from glance __init__
from api __init__    # 导入包就相当于导入其下的__init__.py,所以打印了这行
from policy.py

 

import glance.api.policy.get
glance.api.policy.get()

运行结果:
Traceback (most recent call last):
  File "D:/python/py17/code/py17day05/test.py", line 47, in <module>
    import glance.api.policy.get
ImportError: No module named 'glance.api.policy.get'; 'glance.api.policy' is not a package    

#导入时,. 的左边必须是包名

 

总结如下:

1. 无论是import形式还是from...import形式,凡是在导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉:这是关于包才有的导入语法

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

3. import导入文件时,产生名称空间中的名字来源于文件,import 包,产生的名称空间的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件

 

 

#glance下的__init__.py:
print('from glance __init__')

#api下__init__.py:
print('from api __init__')
import policy.py    

#glance同级的test.py
from glance import api
api.policy.get()

运行test.py结果:
ImportError: No module named 'policy'

 

  我们想通过glance下的test.py导入policy,首先在api的__init__.py下导入了policy.py,结果报错,将导入方式换成如下形式:

#api下的__init__.py:
print('from api __init__')
from glance.api import policy

运行test.py结果:
from glance __init__
from api __init__
from policy.py

  我们可以直接打印这两个文件下的sys.path来对比一下,发现结果是一样的:

 

# glance同级的test.py:
import sys
from glance import api
print('test.py sys.path:%s'%sys.path)
api.policy.get()

# api下的__init__.py:
import sys
print('from api __init__.py')
print('api init sys.path:%s'%sys.path)

from glance.api import policy

#运行test.py结果:
from glance __init__.py
from api __init__.py
api init sys.path:['D:\\python\\python3\\s17day05code', 'D:\\python\\python3', 'D:\\python\\python\\monitor_test', 'C:\\Windows\\system32\\python34.zip', 'C:\\Python34\\DLLs', 'C:\\Python34\\lib', 'C:\\Python34', 'C:\\Python34\\lib\\site-packages', 'C:\\Python34\\lib\\site-packages\\win32', 'C:\\Python34\\lib\\site-packages\\win32\\lib', 'C:\\Python34\\lib\\site-packages\\Pythonwin']
test.py sys.path:['D:\\python\\python3\\s17day05code', 'D:\\python\\python3', 'D:\\python\\python\\monitor_test', 'C:\\Windows\\system32\\python34.zip', 'C:\\Python34\\DLLs', 'C:\\Python34\\lib', 'C:\\Python34', 'C:\\Python34\\lib\\site-packages', 'C:\\Python34\\lib\\site-packages\\win32', 'C:\\Python34\\lib\\site-packages\\win32\\lib', 'C:\\Python34\\lib\\site-packages\\Pythonwin']
from policy.py
执行文件导入其他模块,模块的sys.path都以执行文件为准

 

 

 

  可见导入模块判定路径(sys.path)的标准是以test.py(被执行文件)所在的路径(sys.path)来判断的:

  因为执行文件时,sys.path已经加载到内存中了,内存中已经存在被执行文件的sys.path,执行文件过程中存在导入其它模块的过程,导入其它模块也要参考sys.path,因为内存中已经存在被执行文件的sys.path,所以导入的模块也以这个sys.path为准。

包的绝对导入:

  如果policy想调用同级目录下的versions改如何导入:

#api下的policy.py:
def get():
    print('from policy.py')

import versions
versions.create_resource('conf')

运行policy.py结果:
from version.py:  conf

  可见运行policy.py直接导入同级的versions是没问题的,但是policy.py一般来说是作为模块被人调用的,如果在glance的test.py执行则会报错:

# glance同级的test.py:
from glance.pai import policy

运行test.py结果:
ImportError: No module named 'versions'

  如同之前的例子,执行文件是谁,路径就以谁的为准,所以:

#api下的policy:
def get():
    print('from policy.py')

from glance.api import versions
versions.create_resource('conf')

#glance同级的test.py:
from glance.api import policy

运行test.py结果:
from glance __init__
from api __init__
from version.py:  conf  

  这是如果单独运行policy.py则会报错,因为它在本身的路径下找不到glance,如果即想单独运行,又能够被调用,我们可以加一个if判断:


#api下的policy:
def get():
    print('from policy.py')

if __name__ == '__main__':  # 当脚本运行时
    import versions
    versions.create_resource('a.conf')
else:  # 被导入运行时
    from glance.api import versions
    versions.create_resource('a.conf')

运行policy.py结果:
from version.py:  a.conf

运行test.py结果:
from glance __init__
from api __init__
from version.py:  a.conf  

  同样,policy也可以调用其它子包的模块,如:

#api下的policy.py:
def get():
    print('from policy.py')

from glance.cmd import manage
manage.main()

# glance同级下的test.py:
from glance.api import policy

运行glance同级下的test.py结果:
from glance __init__
from api __init__
from manage.py

 

 

 包的相对导入:

   如果glance包名如果变更的话,调用其它子包模块都要更改?我们可以用相对导入的方式来避免,如:

#api下的policy.py:
def get():
    print('from policy.py')

from ..cmd import manage  # 相对导入
manage.main()

# glance同级下的test.py:
from glance.api import policy

运行glance同级下的test.py结果:
from glance __init__
from api __init__
from manage.py

补充:

  关于*和__all__的使用:

# api下的__init__.py:
print('from api __init__')
__all__=['policy','versions']    # 指定*导入的模块

#api下的policy.py:
def get():
    print('from policy.py')

#api下的versions.py :
def create_resource(conf):
    print('from version.py: ',conf)

# glance 同级的test.py:
from glance.api import *
policy.get()
versions.create_resource('conf')

运行test.py结果:
from glance __init__
from api __init__
from policy.py
from version.py:  conf

  注:__all__只能搭配*使用:

# glance 同级下的test.py:
from glance import api
policy.get()
versions.create_resource('conf')

# 运行test.py结果:
from glance __init__
from api __init__
NameError: name 'policy' is not defined

 

七、正则表达式(re模块)

 

posted @ 2017-05-30 22:51  Dylan_Wu  阅读(207)  评论(0)    收藏  举报