Python基础(协程函数、内置函数、递归、模块和包)

 写在前面



    凭着爱,再回首;



一、协程函数(生成器:yield的表达式形式)

  1.yield 的语句形式: yield 1

    - 这种方式在 Python基础(函数部分)-day04 里面详细介绍过,这里不再赘述;

  2.yield 的表达式形式: x=yield

    - 示例1:使用 next() 方法调用生成器执行,没有给yield传值;

 1 def deco(func):                              # 定义了一个装饰器
 2     def wrapper(*args,**kwargs):
 3         res = func(*args,**kwargs)
 4         next(res)                            # 就是为了先执行一下 next方法
 5         return res
 6     return wrapper
 7 
 8 @deco
 9 def eater(name):
10     print('%s ready to eat' % name)
11     while True:
12         food=yield
13         print("%s start to eat %s" % (name, food))
14 g = eater('alex')                            # 得到一个生成器函数
15 next(g)                                      # 第一次调用 生成器的next方法;(装饰器里提前执行了一次 next方法)
16 next(g)                                      # 第二次调用...
17 
18 ---
19 alex ready to eat                            # 装饰器里调用 next() 的打印结果
20 alex start to eat None                       # 第一次调用 next() 的打印结果
21 alex start to eat None                       # 第二次调用...

    - 示例2:使用 send() 方法调用生成器执行,并给yield传值;

 1 def deco(func):
 2     def wrapper(*args,**kwargs):
 3         res = func(*args,**kwargs)
 4         next(res)
 5         return res
 6     return wrapper
 7 
 8 @deco
 9 def eater(name):
10     print('%s ready to eat' % name)
11     food_list = []
12     while True:
13         food=yield
14         food_list.append(food)
15         print("%s start to eat %s" % (name, food))
16         print("%s have eaten: %s" % (name,food_list))
17 g = eater('alex')
18 g.send('tomato')                    # 第一次调用生成器执行,传入 'tomato' 给 yield,然后由yield赋值给food变量;然后向下执行进入下一次while循环,暂停并等待;
19 g.send('potato')
20 g.send('beef')
21 g.send('rice')
22 
23 ---
24 alex ready to eat
25 alex start to eat tomato
26 alex have eaten: ['tomato']
27 alex start to eat potato
28 alex have eaten: ['tomato', 'potato']
29 alex start to eat beef
30 alex have eaten: ['tomato', 'potato', 'beef']
31 alex start to eat rice
32 alex have eaten: ['tomato', 'potato', 'beef', 'rice']

    注意:next(g) #等同于 g.send(None)

  3.yield表达式形式的应用

 1 #!/usr/bin/python
 2 # -*- coding:utf-8 -*-
 3 
 4 # 实现Linux下的如下命令: grep -rl 'python' path
 5 # 即给出一个目录和patten,递归遍历查找该目录下含有该patten的文件完整路径,如果文件含有多行则需要去重;
 6 
 7 import os
 8 
 9 def init(func):                                            # 定义一个执行 next() 功能的装饰器;
10     def wrapper(*args,**kwargs):
11         res = func(*args,**kwargs)
12         next(res)
13         return res
14     return wrapper
15 @init
16 def search_all_file(target):                               
17     while True:
18         path = yield                                       # 利用send()给yield传值,然后赋值给path
19         g = os.walk(path)                                  # 根据给定path,遍历该目录下所有文件,也包含所有子目录下的文件;
20         for path,_,files in g:
21             for file in files:
22                 target.send(r'%s\%s' % (path,file))        # 利用send()把遍历得到的所有文件传递给下一个生成器的yield;不接受yield的返回值;
23 @init
24 def opener(target):
25     while True:
26         file_to_open = yield                               # 接收调用者传递的文件名,即 search_all_file()函数里send()的参数;
27         with open(file_to_open,encoding='utf-8') as rf:
28             for line in rf:                                # 打开文件,遍历该文件内容;
29                 res = target.send((file_to_open,line))     # 利用send()把该文件名和每次遍历的每行内容传递给下一个生成器的yield;并用res接收返回值;
30                 if res:                                    # 需要注意的一点:send()参数只能是一个值,多个参数则可以以元组的形式传递;
31                     break
32 @init
33 def grep(patten):
34     flag = False                                           # 定义一个标识位
35     while True:
36         the_file,line = yield flag                         # 接收调用者传递的文件名和该文件名下遍历得到的每行内容;
37         flag = False
38         if patten in line:
39             flag = True                                    # 如果某个文件某行内容包含 patten 关键字,则将标识位置为 True,并再下一次调用的时候返回给调用者;
40             print(the_file)                                # 打印匹配到的文件名;
41 
42 path = r'D:softworkPython_17day05a'                   # path=r'path'  -->  r'xxx' 表示绝对 row data,表示不用转意;
43 g = search_all_file(opener(grep('python')))                # 注意这里的调用写法!!!
44 print(g)
45 g.send(path)

二、递归调用

  1.递归的概念

    在函数调用过程中,直接或间接地调用了函数本身,这就是函数的递归调用;

    包括两部分:递推回溯

1 def f1():
2      print('from f1')
3      f1()
4 
5 f1()

  2.Python3中递归的层数限制

1 import sys
2 print(sys.getrecursionlimit())    # 查看默认递归次数上限
3 sys.setrecursionlimit(2000)      # 认为设置递归上限
4 print(sys.getrecursionlimit())
5 
6 ---
7 1000
8 2000

  3.递归特性

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

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

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

    4.堆栈扫盲:http://www.cnblogs.com/lln7777/archive/2012/03/14/2396164.html

    5.尾递归优化:http://egon09.blog.51cto.com/9161406/1842475

  4.递归具体应用

    1.二分法查找某个元素

      前提:输入的可循环对象必须是有序的;

 1 num_list = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35,99]
 2 
 3 def find_num(num_list, number):
 4     if 0 == len(num_list):
 5         print('No exist.')
 6         return 
 7     mid_index = len(num_list)//2
 8     mid_value = num_list[mid_index]
 9     if number > mid_value:
10         num_list = num_list[mid_index+1:]
11         find_num(num_list,number)
12     elif number < mid_value:
13         num_list = num_list[:mid_index]
14         find_num(num_list,number)
15     else:
16         print('Find it.')
17 
18 find_num(num_list,2)

    2.DNS 的递归和迭代查找

      参考:DNS递归查询和迭代查询的区别

    3.Linux深水炸弹

      参考:经典的Fork炸弹

三、内置函数

  1.匿名函数lambda

     - 匿名函数就是不需要显式的指定函数名,定义完就失效了;

 1 f=lambda x,y:x+y                   # lambda作为一个表达式,定义了一个匿名函数,功能就是返回两个参数的和;
 2 print(f)
 3 print(f(1,2))
 4 
 5 print('----------')
 6 def f2(x,y):                       # 函数实现
 7     return x+y
 8 print(f2)
 9 print(f2(1,2))
10 
11 ---
12 <function <lambda> at 0x00000000006CC400>
13 3
14 ----------
15 <function f2 at 0x00000000006CC488>
16 3

    - lambda表达式定义的匿名函数,通常用来和其他函数搭配使用;

    - 示例如下:找出字典中value值对应的key;

 1 # 都是整数的列表可用max取得最大值
 2 l=[3,213,1111,31121]
 3 print(max(l))
 4 
 5 # 字典怎么取值呢?
 6 dic={'k7':10,'k2':100,'k3':30}
 7 print(max(dic))                            # 在对字典进行数据操作的时候,默认只会处理key,而不是value;所以在 'k7' 'k2' 'k3' 里选出最大的一个:'k7'
 8 print(dic[max(dic,key=lambda k:dic[k])])   # key=lambda k:dic[k]  -->  表示更改字典默认处理 value;
 9 
10 ---
11 31121
12 k7
13 100

    - 更多使用见后面例子;

  2.max 和 min

    - 返回最大值/最小值;

 1 def max(*args, key=None): # known special case of max
 2     """
 3     max(iterable, *[, default=obj, key=func]) -> value
 4     max(arg1, arg2, *args, *[, key=func]) -> value
 5     
 6     With a single iterable argument, return its biggest item. The
 7     default keyword-only argument specifies an object to return if
 8     the provided iterable is empty.
 9     With two or more arguments, return the largest argument.
10     """

    - 示例1:max + lambda 实现找出字典中最大value值对应的key

1 dic={'k7':10,'k2':100,'k3':30}
2 max_key = max(dic,key=lambda k:dic[k])
3 print(max_key,dic[max_key])
4 
5 ---
6 k2 100

  3.sorted

    - 返回一个按从小到大排序的列表;

1 dic={'k7':10,'k2':100,'k3':30}
2 print(sorted(dic))
3 res = zip(dic.values(),dic.keys())
4 print(sorted(res))
5 
6 ---
7 ['k2', 'k3', 'k7']
8 [(10, 'k7'), (30, 'k3'), (100, 'k2')]

    - 升序和降序;

1 dic={'k7':10,'k2':100,'k3':30}
2 print(sorted(dic))                                           # 默认是按照字典的key值进行排序
3 print(sorted(dic,key=lambda k:dic[k]))                       # 将字典的key和value翻转,按照value大小进行排序,按照value值的从小到大
4 print(sorted(dic,key=lambda k:dic[k],reverse=True))          # 按照value值的从大到小排序
5 
6 ---
7 ['k2', 'k3', 'k7']
8 ['k7', 'k3', 'k2']
9 ['k2', 'k3', 'k7']    

  4.zip

    - zip函数接受任意多个(包括0个和1个)序列作为参数,返回一个tuple列表;

    - 使用zip把字典的keys和values翻转过来;

 1 dic={'k7':10,'k2':100,'k3':30}
 2 print(type(dic))
 3 for item in dic.items():
 4     print(item)
 5 res_dic = zip(dic.values(),dic.keys())
 6 print(type(res_dic))
 7 # print(next(res_dic))
 8 for item in res_dic:
 9     print(item)
10 
11 ---
12 <class 'dict'>
13 ('k7', 10)
14 ('k2', 100)
15 ('k3', 30)
16 <class 'zip'>
17 (10, 'k7')
18 (100, 'k2')
19 (30, 'k3')

    - 示例2:翻转字典的key和value,并且找出最大的value值;

 1 dic={'k7':10,'k2':100,'k3':30}
 2 
 3 print(max(dic))
 4 res = zip(dic.values(),dic.keys())
 5 print(type(res),res)
 6 print(max(res))
 7 
 8 ---
 9 k7
10 <class 'zip'> <zip object at 0x00000000006C6BC8>
11 (100, 'k2')

    - 示例3:zip 补充

 1 dic={'k7':10,'k2':100,'k3':30}
 2 
 3 res = zip(dic.values(),dic.keys())
 4 print(type(res),res)
 5 print(res.__next__())     # 具有 __next__() 方法,所以是一个迭代器;
 6 print(res.__next__())
 7 print(res.__next__())
 8 # print(res.__next__())   # 抛出 StopIteration异常
 9 
10 ---
11 <class 'zip'> <zip object at 0x0000000000775C08>
12 (10, 'k7')
13 (30, 'k3')
14 (100, 'k2')

    - 示例4:

 1 s='helloo'
 2 l=[1,2,3,4,5,6,7,8]
 3 m=('a','b','c','d')
 4 z=zip(s,l,m)
 5 print(z)
 6 for i in z:
 7     print(i)
 8 
 9 ---
10 <zip object at 0x0000000000B26D48>
11 ('h', 1, 'a')
12 ('e', 2, 'b')
13 ('l', 3, 'c')
14 ('l', 4, 'd')

  5.map

    - 接收一个函数 f 和一个list,并通过把函数 f 依次作用在 list 的每个元素上,得到一个新的 list 并返回;

1     """
2     map(func, *iterables) --> map object
3     
4     Make an iterator that computes the function using arguments from
5     each of the iterables.  Stops when the shortest iterable is exhausted.
6     """

    - 示例:

 1 l=['alex','wupeiqi','yuanhao']
 2 res=map(lambda x:x+'_SB',l)
 3 print(res)
 4 print(list(res))
 5 nums=(2,4,9,10)
 6 res1=map(lambda x:x**2,nums)
 7 print(list(res1))
 8 
 9 ---
10 <map object at 0x00000000007E3400>
11 ['alex_SB', 'wupeiqi_SB', 'yuanhao_SB']
12 [4, 16, 81, 100]

  6.reduce

    - reduce()函数接收的参数和 map()类似,一个函数 f,一个list,但行为和 map()不同,reduce()传入的函数 f 必须接收两个参数,reduce()对list的每个元素反复调用函数f,并返回最终结果值;

 1     """
 2     reduce(function, sequence[, initial]) -> value
 3     
 4     Apply a function of two arguments cumulatively to the items of a sequence,
 5     from left to right, so as to reduce the sequence to a single value.
 6     For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
 7     ((((1+2)+3)+4)+5).  If initial is present, it is placed before the items
 8     of the sequence in the calculation, and serves as a default when the
 9     sequence is empty.
10     """

    - 示例1:

1 from functools import reduce
2 
3 l=[1,2,3,4,5]
4 print(reduce(lambda x,y:x+y,l))
5 
6 ---
7 15

    - 示例2:

1 from functools import reduce
2 
3 l=[1,2,3,4,5]
4 print(reduce(lambda x,y:x+y,l,1000))      # 指定初始值为 1000
5 
6 ---
7 1015

  7.filter

    - filter()函数接收一个函数 f 和一个list,这个函数 f 的作用是对每个元素进行判断,返回 True或 False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list;

1     """
2     filter(function or None, iterable) --> filter object
3     
4     Return an iterator yielding those items of iterable for which function(item)
5     is true. If function is None, return the items that are true.
6     """

    - 示例:

1 l=['alex_SB','wupeiqi_SB','yuanhao_SB','egon']
2 
3 res=filter(lambda x:x.endswith('SB'),l)
4 print(res)
5 print(list(res))
6 
7 ---
8 <filter object at 0x00000000006BA7F0>
9 ['alex_SB', 'wupeiqi_SB', 'yuanhao_SB']

  8.global

    - 返回全局命名空间,比如全局变量名,全局函数名;

    - 在函数内使用global关键字,可以修改全局变量的值

 1 x=1000
 2 def f1():
 3     global x
 4     x=0
 5 
 6 f1()
 7 print(x)
 8 
 9 ---
10 0

  9.进制相关

1 print(bin(3))         # 二进制表示
2 print(hex(17))      # 十六进制表示
3 print(oct(9))         # 八进制表示
4 
5 ---
6 0b11
7 0x11
8 0o11    

  10.字符与数字转换

1 print(ord("A"))                         # "A"字符对应的数值
2 print(chr(65))                          # 数值65对应的字符
3 
4 ---
5 65
6 A

  11.数学运算相关

 1 abs(-5)                          # 取绝对值,也就是5
 2 
 3 round(2.6)                       # 四舍五入取整,也就是3.0
 4 
 5 pow(2, 3)                        # 相当于2**3,如果是pow(2, 3, 5),相当于2**3 % 5
 6 
 7 cmp(2.3, 3.2)                    # 比较两个数的大小
 8 
 9 divmod(9,2)                      # 返回除法结果和余数
10 
11 max([1,5,2,9])                   # 求最大值
12 
13 min([9,2,-4,2])                  # 求最小值
14 
15 sum([2,-1,9,12])                 # 求和

  12.序列操作

1 all([True, 1, "hello!"])         # 是否所有的元素都相当于True值
2 
3 any(["", 0, False, [], None])    # 是否有任意一个元素相当于True值
4 
5 sorted([1,5,3])                  # 返回正序的序列,也就是[1,3,5]
6 
7 reversed([1,5,3])                # 返回反序的序列,也就是[3,5,1]

  13.编译、执行

    - repr,返回对象的字符串表达;

1     """
2     Return the canonical string representation of the object.
3     
4     For many object types, including most builtins, eval(repr(obj)) == obj.
5     """

      - 参考:Python中的repr()函数

1 >>>repr([0,1,2,3])
2 '[0,1,2,3]'
3 >>> repr('Hello')
4 "'Hello'"
5 
6 >>> str(1.0/7.0)
7 '0.142857142857'
8 >>> repr(1.0/7.0)
9 '0.14285714285714285'

    - compile,编译字符串成为code对象;

1 >>> compile("print('Hello')",'test.py','exec')
2 <code object <module> at 0x0000000000A458A0, file "test.py", line 1>
3 >>>

    - eval,解释字符串表达式;

 1 cmd='print("你瞅啥")'
 2 eval(cmd)
 3 
 4 dic="{'a':1,'b':2}"
 5 print(type(dic),dic)
 6 d=eval(dic)
 7 print(type(d),d['a'])
 8 
 9 ---
10 你瞅啥
11 <class 'str'> {'a':1,'b':2}
12 <class 'dict'> 1

    - exec,解释并执行字符串;

1 exec("print('Hello')")
2 
3 ---
4 Hello

  14.enumerate, 枚举,可设定初始值;

 1 res = enumerate([1,2,3],100)
 2 print(type(res),res)
 3 for item in res:
 4     print(item)
 5 
 6 print('----------------')
 7 S = 'cde'
 8 for (index,char) in enumerate(S,1000):
 9     print(index,char)
10 
11 ---
12 <class 'enumerate'> <enumerate object at 0x0000000000B24510>
13 (100, 1)
14 (101, 2)
15 (102, 3)
16 ----------------
17 1000 c
18 1001 d
19 1002 e

  15.hash

1 # 哈希
2 # 1. 只要校验的内容一致,那hash得到结果永远一样
3 # 2. 不可逆
4 # 3. 只要采用的哈希算法一样,那无论被校验的内容有多长,hash的到的结果长度都一样

     - 示例:

1 print(hash('asdfasdfsadf'))
2 print(hash('asdfasdfsadf'))
3 
4 ---
5 5642351931168947998
6 5642351931168947998

  16.id

1     """
2     Return the identity of an object.
3     
4     This is guaranteed to be unique among simultaneously existing objects.
5     (CPython uses the object's memory address.)
6     """

    - 示例:

 1 x=1
 2 y=x
 3 print(id(x))
 4 print(id(y))
 5 print(x is y)  #判断的是身份
 6 
 7 a=1
 8 b=1
 9 print(a == b)  #判断的是值
10 
11 ---
12 1757217232
13 1757217232
14 True
15 True

  17.format

    - 字符串格式化输出,推荐使用,不推荐使用 '%';

  18.slice

    - 构建下标对象 slice;

 1 l=['a','b','c','d','e']
 2 print(l[1:4:2])
 3 s=slice(1,4,2)
 4 print(type(s),s)
 5 print(l[s])
 6 
 7 ---
 8 ['b', 'd']
 9 <class 'slice'> slice(1, 4, 2)
10 ['b', 'd']

  19.其他

1 globals()                        # 返回全局命名空间,比如全局变量名,全局函数名
2 
3 locals()                         # 返回局部命名空间
4 
5 dir()                            # 用来查询一个类或者对象所有属性;
6 
7 help()                           # 用来查询的说明文档;

  20.内置函数列表

参考:Python补充03 Python内置函数清单

四、面向过程编程与函数式编程

  1.面向过程编程

    1.概念:面向过程的程序设计:是一种流水线式的、机械式的过程;

    2.优缺点

      1.优点
        程序结构清晰,复杂的问题分解成多个简单的最小单元,每个单元完成各自的功能;
      2.缺点
        扩展性差,牵一发而动全身;
    3.应用场景
      Linux内核、git、httpd

  2.函数式编程

    1.函数式编程(不同于面向过程编程),是模拟数学意义上的函数;

    2.特性
      1.不允许对外部变量做任何修改
      2.没有循环的概念,所有的循环都是用 尾递归 实现的;
      3.函数式编程语言 与 Python无关;
      ...

五、模块的使用

  1.模块的概念

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

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

  2.模块的分类

    - 1.内置模块

    - 2.自定义模块

    - 3.第三方模块

  3.模块的使用

    - 1.import ... 导入

      - import spam,会将源文件的名称空间'spam'带到当前名称空间中,使用时必须是spam.名字的方式;

      - 执行文件:demo.py,模块文件:spam.py

      - 模块示例:

 1 #!/usr/bin/python
 2 # -*- coding:utf-8 -*-
 3 
 4 #spam.py 模块
 5 print('from the spam.py')
 6 
 7 money=1000
 8 
 9 def read1():
10     print('spam->read1->money',money)
11 def read2():
12     print('spam->read2 calling read')
13     read1()
14 def change():
15     global money
16     money=0

      - 调用示例1:

 1 #!/usr/bin/python
 2 # -*- coding:utf-8 -*-
 3 
 4 import spam
 5 
 6 spam.read2()
 7 print(spam.money)
 8 
 9 ---
10 from the spam.py                  # import spam  这一行调用执行了 print('from the spam.py')
11 spam->read2 calling read          # 可以通过  模块名 + '.' 的方式调用模块里的函数和变量
12 spam->read1->money 1000
13 1000

      - 调用示例2:

 1 #!/usr/bin/python
 2 # -*- coding:utf-8 -*-
 3 
 4 import spam as x
 5 x.read2()                         # 可以以别名的形式进行调用
 6 print(x.money)
 7 
 8 ---
 9 from the spam.py
10 spam->read2 calling read
11 spam->read1->money 1000
12 1000

       - 调用示例3:

 1 import spam
 2 
 3 money = 99
 4 def read1():
 5     print('----------->>>')
 6 print(spam.money)
 7 spam.read1()
 8 read1()                        # read1() 与 spam.read1() 不冲突
 9 print(money)                   # money 与 spam.money 不冲突
10 
11 ---
12 from the spam.py
13 1000
14 spam->read1->money 1000
15 ----------->>>
16 99

      - 调用示例4:

 1 #!/usr/bin/python
 2 # -*- coding:utf-8 -*-
 3 
 4 import spam
 5 
 6 money = 9
 7 print(spam.money)
 8 spam.change()                   # 调用spam模块的change()函数,修改spam中全局空间的money值
 9 print(spam.money)
10 print(money)                    # 执行文件中的money值没有被修改
11 
12 ---
13 from the spam.py
14 1000
15 0
16 9

      - 导入模块干了那些事情? 

        - 1.为源文件(spam模块)产生新的名称空间;

        - 2.以新建的名称空间为全局名称空间,执行文件(spam.py)的代码;

          - 事实上函数定义也是“被执行”的语句,模块级别函数定义的执行将函数名放入模块全局名称空间表,用globals()可以查看;

        - 3.拿到一个模块名spam,指向spam.py产生的名称空间;

    - 2.from ... import ... 导入

      - from 语句相当于import,也会创建新的名称空间;但是将spam中的名字直接导入到当前的名称空间中,在当前名称空间中,直接使用名字就可以了;

      - 优点:方便,不用加前缀;

      - 缺点:容易跟当前文件的名称空间冲突

      - 示例1:

 1 from spam import money,read1,read2,change
 2 
 3 print(money)
 4 read1()                         # 在当前位置直接使用read1和read2就好了,执行时,仍然以spam.py文件全局名称空间;
 5 change()
 6 read1()
 7 
 8 ---
 9 from the spam.py
10 1000
11 spam->read1->money 1000
12 spam->read1->money 0

      - 示例2:

1 # 导入的函数read1,执行时仍然回到spam.py中寻找全局变量money
2 from spam import read1
3 money=20
4 read1()
5 
6 ---
7 from the spam.py
8 spam->read1->money 1000
 1 # 导入的函数read2,执行时需要调用read1(),仍然回到spam.py中找read1()
 2 from spam import read2
 3 def read1():
 4     print('==========')
 5 read2()
 6 
 7 ---
 8 from the spam.py
 9 spam->read2 calling read
10 spam->read1->money 1000

      - 示例3:

1 # 导入的函数read1,被当前位置定义的read1覆盖掉了
2 from spam import read1
3 def read1():
4     print('==========')
5 read1()
6 
7 ---
8 from the spam.py
9 ==========

      - 示例4:

1 from spam import money,read1
2 money=200                      # 将当前位置的名字money绑定到了100
3 print(money)                   # 打印当前的名字
4 read1()                        # 读取spam.py中的名字money,仍然为1000
5 
6 ---
7 from the spam.py
8 200
9 spam->read1->money 1000

      - 示例5:

1 # 也支持 as
2 from spam import read1 as read

      - 导入模块干了那些事情? 

        - 1.产生新的名称空间;

        - 2.以新建的名称空间为全局名称空间,执行文件的代码;

        - 3.直接拿到就是spam.py产生的名称空间中名字;

    - from ... import * 导入

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

      - 示例1:

 1 from spam import *
 2 print(money)
 3 read2()
 4 change()
 5 read2()
 6 
 7 ---
 8 from the spam.py
 9 1000                         # 打印了spam里的money值
10 spam->read2 calling read     # 第一次调用 read2()
11 spam->read1->money 1000      # money -->  1000
12 spam->read2 calling read     # 第二次调用 read2()
13 spam->read1->money 0         # money -->  0  ,因为这之前调用了 change(),修改了money在spam名称空间的值;

      - 示例2:这里有疑问!!!

1 from spam import *
2 print(money)
3 change()
4 print(money)
5 
6 ---
7 from the spam.py
8 1000                          # 第一次打印spam里的money值,然后紧接着调用了change()函数修改了money
9 1000                          # 第二次打印money值,这是在修改之后的值,但是还是原来的值?????

      - 可以使用__all__来控制 * 

      - __all__ 是和调用者里的 * 对应的;其他方式调用时无效;

 1 # 在spam.py 中新增加一行:__all__=['money','read1'] 
 2 # 这样在另外一个文件中用from spam import *  就这只能导入列表中规定的两个名字
 3 
 4 #!/usr/bin/python
 5 # -*- coding:utf-8 -*-
 6 
 7 #spam.py
 8 print('from the spam.py')
 9 
10 money=1000
11 
12 def read1():
13     print('spam->read1->money',money)
14 def read2():
15     print('spam->read2 calling read')
16     read1()
17 def change():
18     global money
19     money=0
20 
21 __all__=['money','read1']

      - 示例3:

 1 from spam import *
 2 
 3 print(money)
 4 read1()                     # 调用 spam.py 中 __all__ 中写好的函数和变量是没问题的;
 5 
 6 ---
 7 from the spam.py
 8 1000
 9 spam->read1->money 1000

      - 示例4:

 1 from spam import *
 2 
 3 print(money)
 4 read1()
 5 change()                    # change 没有在 __all__ 里,所以调用这个会报错
 6 
 7 ---
 8 from the spam.py
 9 1000
10 spam->read1->money 1000
11 Traceback (most recent call last):
12   File "D:/soft/work/Python_17/day05/模块/demo.py", line 75, in <module>
13     change()
14 NameError: name 'change' is not defined

  4.模块搜索路径

1 python解释器在启动时会自动加载一些模块,可以使用sys.modules查看;
2 
3 在第一次导入某个模块时(比如spam),会先检查该模块是否已经被加载到内存中(当前执行文件的名称空间对应的内存),如果有则直接引用;
4 如果没有,解释器则会查找同名的内置模块;
5 如果还没有找到就从sys.path给出的目录列表中依次寻找spam.py文件。
6 
7 所以总结模块的查找顺序是:
8     内存中已经加载的模块 --> 内置模块 --> sys.path路径中包含的模块
 1 import spam    ===>>   第一次导入,肯定不在内存中,也不是内置的,所以只能去 sys.path 中去找;
 2 
 3 
 4 如果在其他级别的路径下,则需要把该路径加入到 sys.path 
 5 
 6 import sys
 7 sys.path.append(r'path')
 8 sys.path.insert(0,r'path')
 9 
10 或者把相关的py文件移动到已有的sys.path某个路径下

     - 示例:查看和追加到sys.path

1 import sys
2 
3 print(sys.path)
4 sys.path.append(r'D:softworkPython_17day06')
5 print(sys.path)
6 
7 ---
8 ['D:\soft\work\Python_17\day05\模块', 'D:\soft\work\Python_17', 'D:\soft\work\python35\python35.zip', 'D:\soft\work\python35\DLLs', 'D:\soft\work\python35\lib', 'D:\soft\work\python35', 'D:\soft\work\python35\lib\site-packages']
9 ['D:\soft\work\Python_17\day05\模块', 'D:\soft\work\Python_17', 'D:\soft\work\python35\python35.zip', 'D:\soft\work\python35\DLLs', 'D:\soft\work\python35\lib', 'D:\soft\work\python35', 'D:\soft\work\python35\lib\site-packages', 'D:\soft\work\Python_17\day06']
查看 sys.path

  5.模块的两种使用方式

    - 我们可以通过模块的全局变量 __name__ 来查看模块名;

    - 当做脚本直接执行时:

 1 #spam.py
 2 print('from the spam.py')
 3 money=1000
 4 
 5 def read1():
 6     print('spam->read1->money',money)
 7 def read2():
 8     print('spam->read2 calling read')
 9     read1()
10 def change():
11     global money
12     money=0
13 
14 print(__file__)                  # __file__ 就是文件名
15 print(__name__)                  # 当spam.py当做脚本直接执行时, __name__ 就是 __main__
16 
17 ---
18 from the spam.py
19 D:/soft/work/Python_17/day05/模块/spam.py
20 __main__

    - 当做模块被其他人导入使用时:

1 # demo.py, 导入spam模块,spam.py 就是上面那个
2 
3 import spam                       # 当spam.py当做模块被导入时,__name__ 就是 模块名,即spam
4 
5 ---
6 from the spam.py
7 D:softworkPython_17day05模块spam.py
8 spam                              

    - 应用:写好的模块代码都应该加上, if __name__ == '__main__' 这个代码块,然后把所有的有显示的操作都应该写在这里面;

      - 当自己测试/当做脚本执行的时候就可以正常执行并显示;

      - 当作为模块被其他人导入的时候也可以避免这些显示的操作被执行到;

    - 示例:

 1 #spam.py
 2 print('from the spam.py')
 3 money=1000
 4 
 5 def read1():
 6     print('spam->read1->money',money)
 7 def read2():
 8     print('spam->read2 calling read')
 9     read1()
10 def change():
11     global money
12     money=0
13 
14 if __name__ == '__main__':
15     print('This is spam module, running as scripts')

  6.dir()函数

    - 内建函数dir是用来查找模块中定义的名字,返回一个有序字符串列表;

 1 import spam
 2 res = dir(spam)
 3 print(type(res),res)
 4 
 5 import builtins
 6 res2 = dir(builtins)
 7 print(type(res2),res2)
 8 
 9 ---
10 from the spam.py
11 <class 'list'> ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'change', 'money', 'read1', 'read2']
12 <class 'list'> ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
View Code

  7.扩展

    - 我们自定义的模块名不应该与系统内置模块重名;

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

    - 考虑到性能的原因,每个模块只被导入一次,放入字典 sys.modules 中,如果你改变了模块的内容,你必须重启程序,python不支持重新加载或卸载之前导入的模块;

1 import sys
2 import spam
3 print(sys.modules)
4 
5 ---
6 from the spam.py
7 {'nt': <module 'nt' (built-in)>, '_frozen_importlib_external': <module '_frozen_importlib_external' (frozen)>, '_sitebuiltins': <module '_sitebuiltins' from 'D:\soft\work\python35\lib\_sitebuiltins.py'>, 'encodings.aliases': <module 'encodings.aliases' from 'D:\soft\work\python35\lib\encodings\aliases.py'>, 'sysconfig': <module 'sysconfig' from 'D:\soft\work\python35\lib\sysconfig.py'>, 'builtins': <module 'builtins' (built-in)>, '_codecs': <module '_codecs' (built-in)>, 'errno': <module 'errno' (built-in)>, '_bootlocale': <module '_bootlocale' from 'D:\soft\work\python35\lib\_bootlocale.py'>, '_multibytecodec': <module '_multibytecodec' (built-in)>, '_io': <module 'io' (built-in)>, 'io': <module 'io' from 'D:\soft\work\python35\lib\io.py'>, 'stat': <module 'stat' from 'D:\soft\work\python35\lib\stat.py'>, '_thread': <module '_thread' (built-in)>, 'encodings.latin_1': <module 'encodings.latin_1' from 'D:\soft\work\python35\lib\encodings\latin_1.py'>, 'encodings.gbk': <module 'encodings.gbk' from 'D:\soft\work\python35\lib\encodings\gbk.py'>, 'os.path': <module 'ntpath' from 'D:\soft\work\python35\lib\ntpath.py'>, 'spam': <module 'spam' from 'D:\soft\work\Python_17\day05\模块\spam.py'>, 'site': <module 'site' from 'D:\soft\work\python35\lib\site.py'>, 'abc': <module 'abc' from 'D:\soft\work\python35\lib\abc.py'>, 'os': <module 'os' from 'D:\soft\work\python35\lib\os.py'>, 'encodings.utf_8': <module 'encodings.utf_8' from 'D:\soft\work\python35\lib\encodings\utf_8.py'>, 'ntpath': <module 'ntpath' from 'D:\soft\work\python35\lib\ntpath.py'>, '_codecs_cn': <module '_codecs_cn' (built-in)>, 'codecs': <module 'codecs' from 'D:\soft\work\python35\lib\codecs.py'>, 'zipimport': <module 'zipimport' (built-in)>, '_signal': <module '_signal' (built-in)>, '_frozen_importlib': <module '_frozen_importlib' (frozen)>, '__main__': <module '__main__' from 'D:/soft/work/Python_17/day05/模块/demo.py'>, 'winreg': <module 'winreg' (built-in)>, 'genericpath': <module 'genericpath' from 'D:\soft\work\python35\lib\genericpath.py'>, '_locale': <module '_locale' (built-in)>, '_weakrefset': <module '_weakrefset' from 'D:\soft\work\python35\lib\_weakrefset.py'>, '_warnings': <module '_warnings' (built-in)>, 'sys': <module 'sys' (built-in)>, 'marshal': <module 'marshal' (built-in)>, 'encodings': <module 'encodings' from 'D:\soft\work\python35\lib\encodings\__init__.py'>, '_collections_abc': <module '_collections_abc' from 'D:\soft\work\python35\lib\_collections_abc.py'>, '_imp': <module '_imp' (built-in)>, '_weakref': <module '_weakref' (built-in)>, 'encodings.mbcs': <module 'encodings.mbcs' from 'D:\soft\work\python35\lib\encodings\mbcs.py'>, '_stat': <module '_stat' (built-in)>}
查看当前 sys.modules

参考:egon老师总结的模块与包

六、包的使用

  1.包的概念

    - 包是包含 __init__.py 文件的文件夹;导入包就是导入包下的 __init__.py 文件

    - 用途:从目录级别(文件夹级别)组织模块;

    - 包相当于一个大的模块,本质还是给使用者导入来使用的;

1     导入包,是以包下 __init__.py 为准:
2         因为导入模块的时候需要创建新的名称空间;
3         但import 包,包是一个目录而非文件,所以就没办法创建新的名称空间,
4         所以需要在包下有一个 __init__.py 文件,创建新的名称空间;
5 
6     导入的时候,  '.' 的左边必须是包!!!
7     import bao.bao.file  导入的是一串,不仅仅是导入file,即会依次执行包下的__init__.py文件,可以实验验证:在包以及子包的__init__.py文件中加入print语句;见下面例子;

    - 示例,包名:glance

 1 glance/                   #Top-level package
 2 
 3 ├── __init__.py           #Initialize the glance package
 4 
 5 ├── api                   #Subpackage for api
 6 
 7 │   ├── __init__.py
 8 
 9 │   ├── policy.py
10 
11 │   └── versions.py
12 
13 ├── cmd                   #Subpackage for cmd
14 
15 │   ├── __init__.py
16 
17 │   └── manage.py
18 
19 └── db                    #Subpackage for db
20 
21     ├── __init__.py
22 
23     └── models.py

    - 文件内容

 1 # glance 的 __init__.py
 2 print('from glance level.')
 3 
 4 # db 的 __init__.py
 5 print('from glance -> db level.')
 6 
 7 # models.py
 8 def register_models(engine):
 9     print('from models.py: ',engine)
10 
11 print('from glance -> db -> models.')
12 
13 # policy.py
14 def get():
15     print('from policy.py')
16 
17 # versions.py
18 def create_resource(conf):
19     print('from version.py: ',conf)
20 
21 # manage.py
22 def main():
23     print('from manage.py')
24 

    - 示例1:import ... 导入

 1 # packge.py ,和glance同级别的文件
 2 
 3 import glance.db.models           # 导入的是一串,不仅仅是models,所以可以看打印输出的结果;
 4 
 5 glance.db.models.register_models('mysql')
 6 # models.register_models('mysql')  # NameError: name 'models' is not defined   即不能只用 models. 调用,需要写全
 7 ---
 8 from glance level.
 9 from glance -> db level.
10 from glance -> db -> models.
11 from models.py:  mysql

    - 示例2:支持 as 别名

1 import glance.db.models as models
2 
3 models.register_models('mysql') 
4 
5 ---
6 from glance level.
7 from glance -> db level.
8 from glance -> db -> models.
9 from models.py:  mysql

    - 示例3:from ... import ... 导入

 1 from glance.db import models                     # 可以导入模块
 2 models.register_models('ldap')
 3 
 4 print('------------------>>>>>')
 5 
 6 from glance.db.models import register_models     # 可以导入模块中的方法,一步到位;
 7 register_models('mysql')
 8 
 9 ---
10 from glance level.
11 from glance -> db level.
12 from glance -> db -> models.
13 from models.py:  ldap
14 ------------------>>>>>                          
15 from models.py:  mysql
# 凡是在导入时(而非使用时)带点的,点的左边都必须是一个包名;

# from ... import ... 方式导入,import后的东西必须是明确的不能带点,否则会有语法错误,如:from a import b.c是错误语法

# 不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码

    - 示例4:

 1 import glance
 2 
 3 glance.api.policy.get()        # 报错:找不到glance下的api
 4 
 5 ---
 6 from glance level.
 7 Traceback (most recent call last):
 8   File "D:/soft/work/Python_17/day05/包/packge.py", line 21, in <module>
 9     glance.api.policy.get()
10 AttributeError: module 'glance' has no attribute 'api'

      - 前面说过,导入包就是导入包下的 __init__.py,本例中实际上就是导入 glance下的 __init__.py 文件;

                 - 然而glance下__init__.py里面只有这一句:print('from glance level.'),所以肯定找不到 api,所以会报错!!!

    - 示例5:那怎么解决上面那个问题呢?既然import glance是导入执行下面的 __init__.py,那么我们就可以想到在这个 __init__.py 做一些操作了;

 1 # glance下的__init__.py文件与 api文件夹是同一个目录级别
 2 # 所以我们想到在里面加一行:import api,如下:
 3 import api
 4 print('from glance level.')
 5 
 6 # packge.py ,即执行文件
 7 import glance
 8 
 9 glance.api.policy.get()
10 
11 ---
12 Traceback (most recent call last):
13   File "D:/soft/work/Python_17/day05/包/packge.py", line 24, in <module>
14     import glance
15   File "D:softworkPython_17day05包glance\__init__.py", line 4, in <module>
16     import api
17 ImportError: No module named 'api'                        # 报错的地方是在 __init__.py里import api 这一步,说是找不到api,即路径找得不对;

    - 示例6:

 1 # 上面报错路径找得不对;这就回到了模块搜索路径的问题了:
 2 # 内存 --> 内置 --> sys.path
 3 # 很明显是 sys.path 没有找到api这个模块;
 4 # 原因:执行文件是packge.py,而packge.py这个文件是和glance目录同级别的,而api是glance目录下的一个子目录,所以找不到;
 5 # 验证这个想法:
 6 # glance目录下的 __init__.py
 7 print('from glance level.')
 8 import sys
 9 print(sys.path)
10 
11 # packge.py,即执行文件
12 import glance
13 
14 ---
15 from glance level.
16 ['D:\soft\work\Python_17\day05\包', 'D:\soft\work\Python_17', 'D:\soft\work\python35\python35.zip', 'D:\soft\work\python35\DLLs', 'D:\soft\work\python35\lib', 'D:\soft\work\python35', 'D:\soft\work\python35\lib\site-packages']

# 很显然在 __init__.py里面打印的sys.path是执行文件packge.py的sys.path

      - 优化:

 1 # glance 下的 __init__.py
 2 print('from glance level.')
 3 import glance.api
 4 
 5 # packge.py
 6 import glance
 7 
 8 print(glance.api)                # 由打印结果可知,调用 glance.api 就是在执行 glance下api下的 __init__.py 文件
 9 
10 ---
11 from glance level.
12 from glance -> api level.
13 <module 'glance.api' from 'D:\soft\work\Python_17\day05\包\glance\api\__init__.py'>

      - 继续优化:

 1 # glance下的 api 下的 __init__.py
 2 print('from glance -> api level.')
 3 
 4 import glance.api.policy
 5 
 6 # policy.py
 7 def get():
 8     print('from policy.py')
 9 
10 # glance 下的 __init__.py
11 print('from glance level.')
12 import glance.api
13 
14 #packge.py,即执行文件,与glance目录同级
15 import glance
16 
17 print(glance.api)
18 print(glance.api.policy)
19 glance.api.policy.get()              # 执行成功;
20 
21 ---
22 from glance level.
23 from glance -> api level.
24 <module 'glance.api' from 'D:\soft\work\Python_17\day05\包\glance\api\__init__.py'>
25 <module 'glance.api.policy' from 'D:\soft\work\Python_17\day05\包\glance\api\policy.py'>
26 from policy.py

    - 示例6:导入包就是导入包下的 __init__.py

 1 # 修改glance下的 __init__.py 文件
 2 print('from glance level.')
 3 
 4 x = 1
 5 dic = {
 6     'k1':'v1',
 7     'k2':'v2',
 8 }
 9 
10 # packge.py,即执行文件
11 import glance
12 
13 print(glance.x)
14 print(glance.dic)
15 
16 ---
17 from glance level.
18 1
19 {'k1': 'v1', 'k2': 'v2'}

    - 示例7:把子目录下的模块名字导入到glance的名称空间中

 1 # policy.py
 2 def get():
 3     print('from policy.py')
 4 
 5 # glance 下的 __init__.py
 6 print('from glance level.')
 7 
 8 from glance.api import policy
 9 
10 # packge.py
11 import glance                 # import glance 就是执行glance下的 __init__.py文件,即通过 from glance.api import policy 得到了 policy 这个模块名字,并且是属于glance名称空间的12 
13 glance.policy.get()           # 所以可以直接用 glance.policy.get() 形式调用 policy模块里的方法;
14 
15 ---
16 from glance level.
17 from glance -> api level.
18 from policy.py

      - 进一步优化:直接使用 glance.get() ,省去中间的模块名,这样也极大的降低了使用者的学习成本;

 1 # glance 下的__init__.py
 2 print('from glance level.')
 3 
 4 from glance.api.policy import get       # 其实很简单,上个例子中是把policy这个模块名字拿到的 glance名称空间中,这里只要把policy下的get()方法拿到glance的名称空间中就可以了;
 5 
 6 # packge.py
 7 import glance
 8 
 9 glance.get()
10 
11 ---
12 from glance level.
13 from glance -> api level.
14 from policy.py

  3.包的两种导入方式

    - 包的绝对导入

 1 # glance目录下的api目录下有 policy.py 和 verisions.py
 2 
 3 # policy.py
 4 def get():
 5     print('from policy.py')
 6 
 7 if __name__ == '__main__':                                 # 直接执行 policy.py 用的
 8     from versions import create_resource                   # 引用同目录级别的versions.py(policy.py 和 versions.py都在 api 子目录下)
 9     create_resource('nginx.conf.inner')
10 else:                                                      # policy被使用者导入时用的
11     from glance.api.versions import create_resource        # 必须要以 glance开头,因为包名就是 glance,即绝对导入
12     create_resource('nginx.conf')
13 
14 # versions.py
15 def create_resource(conf):
16     print('from version.py: ',conf)
17 
18 # glance下的 __init__.py
19 print('from glance level.')
20 
21 # packge.py,与glance同级的文件
22 import glance.api.policy
23 
24 ---
25 执行 packge.py 的结果:
26 from glance level.
27 from glance -> api level.
28 from version.py:  nginx.conf
29 
30 执行 policy.py 的结果:
31 from version.py:  nginx.conf.inner

    - 包的相对导入,有问题???

 1 # packge.py 和glance同级别的执行文件
 2 import glance.api.policy
 3 
 4 # glance下的 __init__.py
 5 print('from glance level.')
 6 
 7 # policy.py (在api子目录下)
 8 def get():
 9     print('from policy.py')
10 
11 # api下的 __init__.py
12 print('from glance -> api level.')
13 
14 
15 if __name__ == '__main__':                   # 作为执行文件,尝试导入db下的models模块
16     from ..db import models                  # 直接右键执行 policy.py 的时候,尝试去导入在 db 目录下的 model.py 模块;因为 db 和 api 这两个子目录是同一级别的,所以使用 ..db;
17     models.register_models('mysql.inner')
18 else:                                        # 作为模块,被使用者导入
19     from ..db.models import register_models  # 采用 .. 的方式导入,即相对导入;避免了因为改变包名导致包不可用的情况;
20     register_models('mysql')
21 
22 # db 下的 __init__.py
23 print('from glance -> db level.')
24 
25 # models.py (在 db 子目录下)
26 def register_models(engine):
27     print('from models.py: ',engine)
28 
29 print('from glance -> db -> models.')
30 
31 ---
32 # 执行 packge.py 的结果:
33 from glance level.
34 from glance -> api level.
35 from glance -> db level.
36 from glance -> db -> models.
37 from models.py:  mysql
38 
39 # 执行 policy.py 的结果:
40 Traceback (most recent call last):
41   File "D:/soft/work/Python_17/day05/包/glance/api/policy.py", line 9, in <module>
42     from ..db import models
43 SystemError: Parent module '' not loaded, cannot perform relative import      # 右键执行 policy.py 的时候,from ..db import models报错

      - 优化,即 将policy.py的上一级目录追加入到 sys.path里面:

 1 # 更新 policy.py 的代码
 2 def get():
 3     print('from policy.py')
 4 
 5 if __name__ == '__main__':                                                             # 作为执行文件
 6     import os,sys
 7     print(__file__)                                                                    # 打印文件名
 8     print(os.path.abspath(__file__))                                                   # 打印文件的绝对路径
 9     parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))           # 取得policy.py文件的上一级目录的绝对路径:policy.py所在路径是 xxxglanceapi , 上一级就是 xxxglance
10     print(parent_dir)
11     sys.path.append(parent_dir)                                                        # 追加到 sys.path 列表中
12     print('------->>>')
13     print(sys.path)
14     print('------->>>')
15     from db import models                                                              # 追加完 xxxglance 之后,即可以采用 from db import models 的方式导入,而不是 from ..db import models16     models.register_models('mysql.inner')
17 else:                                                                                  # 作为模块,被其他人导入使用
18     from ..db.models import register_models                                            # from ..db.models import register_models ,即包的相对导入19     register_models('mysql')
20 
21 # models.py
22 def register_models(engine):
23     print('from models.py: ',engine)
24 
25 print('from glance -> db -> models.')
26 
27 # packge.py
28 import glance.api.policy
29 
30 ---
31 # 执行 packge.py 的结果:
32 from glance level.
33 from glance -> api level.
34 from glance -> db level.
35 from glance -> db -> models.
36 from models.py:  mysql
37 
38 # 执行 policy.py 的结果:
39 D:/soft/work/Python_17/day05/包/glance/api/policy.py
40 D:softworkPython_17day05包glanceapipolicy.py
41 D:softworkPython_17day05包glance
42 ------->>>
43 ['D:\soft\work\Python_17\day05\包\glance\api', 'D:\soft\work\Python_17', 'D:\soft\work\python35\python35.zip', 'D:\soft\work\python35\DLLs', 'D:\soft\work\python35\lib', 'D:\soft\work\python35', 'D:\soft\work\python35\lib\site-packages', 'D:\soft\work\Python_17\day05\包\glance']
44 ------->>>
45 from glance -> db level.
46 from glance -> db -> models.
47 from models.py:  mysql.inner

  4.包导入时的搜索路径

1 导入时的搜索路径:内存 -> 内置 -> sys.path
2 注意:sys.path 是以执行文件为准的!!!
3     可以通过实验验证:test.py 导入一个包bbb里的一个文件a,bbb目录下还有b.py;这时候如果a.py要导入b.py就容易出问题
4     涉及到绝对导入和相对导入的问题:
5         包的绝对导入,以顶级包名为准
6         包的相对导入:. .. ...

  5. from ... import * 和 __all__=[] 

    - 包结构:

 1 glance/                   #Top-level package
 2 
 3 ├── __init__.py           #Initialize the glance package
 4 
 5 ├── api                   #Subpackage for api
 6 
 7 │   ├── __init__.py
 8 
 9 │   ├── policy.py
10 
11 │   └── versions.py
12 
13 ├── cmd                   #Subpackage for cmd
14 
15 │   ├── __init__.py
16 
17 │   └── manage.py
18 
19 └── db                    #Subpackage for db
20 
21     ├── __init__.py
22 
23     └── models.py
glance包结构

    - 示例,结合之前的一些知识,再加上 __all__ 和 * ,做如下demo:

 1 # glance下的 __init__.py
 2 print('from glance level.')
 3 
 4 # api 下的 __init__.py
 5 print('from glance -> api level.')
 6 
 7 def func():
 8     print('glance -> api -> __init__.py -> func()')
 9 
10 __all__ = ['func','policy','versions']                               # 包含了 __init__.py里的func, api下的policy和versions 
11 
12 # db 下的 __init__.py
13 print('from glance -> db level.')
14 
15 # policy.py
16 def get():
17     print('from policy.py')
18 
19 if __name__ == '__main__':
20     import os,sys
21     print(__file__)
22     print(os.path.abspath(__file__))
23     parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
24     print(parent_dir)
25     sys.path.append(parent_dir)
26     print('------->>>')
27     print(sys.path)
28     print('------->>>')
29     from db import models
30     models.register_models('mysql.inner')
31 else:
32     from ..db.models import register_models
33     register_models('mysql')
34 
35 # versions.py
36 def create_resource(conf):
37     print('from version.py: ',conf)
38 
39 # models.py
40 def register_models(engine):
41     print('from models.py: ',engine)
42 
43 print('from glance -> db -> models.')
44 
45 # packge.py                                      # 执行文件
46 from glance.api import *                         # 导入glance.api下的 * ,即对应的是glance.api目录下的 __init__.py 里定义的 __all__
47 
48 print('------>>>')
49 func()                                           # 因为已经把 func、 policy 和 versions 导入了packge的名称空间,所以可以直接拿来执行
50 policy.get()
51 versions.create_resource('a.conf')
52 
53 ---
54 执行packge.py结果如下:
55 from glance level.
56 from glance -> api level.
57 from glance -> db level.
58 from glance -> db -> models.
59 from models.py:  mysql
60 ------>>>
61 glance -> api -> __init__.py -> func()
62 from policy.py
63 from version.py:  a.conf

   - __all__中没定义的情况:

 1 # 如果 glance.api下的 __init__.py 里的__all__如下所示
 2 __all__ = ['func','policy']
 3 
 4 # packge.py
 5 versions.create_resource('a.conf')
 6 
 7 ---
 8 执行报错:
 9 Traceback (most recent call last):
10   File "D:/soft/work/Python_17/day05/包/packge.py", line 41, in <module>
11     versions.create_resource('a.conf')
12 NameError: name 'versions' is not defined

七、练习

要求:

1     函数+正则===》计算器
2 -1+(1+2*3-(-4/3)+10.3/2)

代码实现:

参考:武Sir的计算器源码

作者:Standby一生热爱名山大川、草原沙漠,还有妹子
出处:http://www.cnblogs.com/standby/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文地址:https://www.cnblogs.com/standby/p/6921955.html