Python-模块和包

今天咱们来聊聊模块和包python

一.模块mysql

  首先,咱们先看一个老生常谈的问题,什么是模块,模块就是一个包含了python定义和声明的文件,文件名就是模块的名字加上.py后缀,欢聚话说咱们目前写的全部的py文件均可以当作是一个模块可是咱们import加载的模块一共分红四个通用类别:sql

  1. 使用pyhton编写的py文件json

  2. 已被变异为共享库或者DLL或C或者C++的扩展api

  3. 包好一组模块的包. app

  4. 使用c编写并链接到python解释器的内置模块dom

  为何要使用模块? 为了咱们写的代码能够重用,不至于把全部的代码都写在一个文件内. 当项目规模比较小的时候,彻底可使用一个py搞定整个项目的开发,可是若是是一个很是庞大的项目. 此时就必需要把相关的功能进行分离,以便咱们的平常维护,以及新项目的开发. 函数

  如何使用模块? 咱们已用过不少模块了,导入模块有两种方式学习

    1. import 模块测试

    2. from xxx import xxxx

二.import

  首先咱们先看import, 在使用import的时候, 咱们先建立一个yitian.py. 在该文件中建立一些武林前辈和一些打斗场景, 代码以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
print ( "片头曲. 啊! 啊~ 啊! 啊. 啊啊啊啊啊啊啊..." )
 
main_person_man  =  "张无忌"
main_person_woman  =  "赵敏"
 
low_person_man_one  =  "成昆"
low_person_man_two  =  "周芷若"
 
def  fight_on_light_top():
     print ( "光明顶大战" , main_person_man,  "破坏了" , low_person_man_one,  "的大阴谋" )
 
def  fight_in_shaolin():
     print ( "少林寺大战" , main_person_man,  "破坏了" , low_person_man_two, "的大阴
谋")

  接下来,金庸上场

1
2
3
4
5
6
7
import  yitian
 
print (yitian.main_person_man)  # 使用模块中定义好的名字
print (yitian.low_person_man_one)
 
yitian.fight_in_shaolin()  # 调用模块中的函数
yitian.fight_on_light_top()

  此时咱们在金庸模块中引入了yitian模块. 

  在Python中模块是不可以重复导入的,当重复导入模块时,系统会根据sys.modules来判断该模块是否已经导入了,若是已经导入,则不会重复导入

1
2
3
4
5
6
7
8
9
import  sys
print (sys.modules.keys())  # 查看导入的模块.
 
import  yitian  # 导入模块. 此时会默认执行该模块中的代码
import  yitian  # 该模块已经导入过了,不会重复执行代码
import  yitian
import  yitian
import  yitian
import  yitian

  导入模块的时候都作了些什么? 首先,在导入模块的一瞬间,python解释器会先经过sys.modules来判断该模块是否已经导入了该模块,若是已经导入了则再也不导入,若是该模块还未导入过,则系统会作三件事.

    1. 为导入的模块创立新的名称空间

    2. 在新建立的名称空间中运行该模块中的代码

    3. 建立模块的名字,并使用该名称做为该模块在当前模块中引用的名字.

   咱们可使用globals来查看模块的名称空间

1
2
3
4
5
6
7
8
9
print ( globals ())
打印结果:
{ '__name__' '__main__' '__doc__' None '__package__' None ,
'__loader__' : <_frozen_importlib_external.SourceFileLoader  object  at
0x10bbcf438 >,  '__spec__' None ,   '__annotations__' : {},  '__builtins__' :
<module  'builtins'  (built - in )>,  '__file__' :
'/Users/sylar/PycharmProjects/oldboy/模块/模块/⾦庸.py' '__cached__' None ,
'yitian' : <module  'yitian'  from  ' / Users / sylar / PycharmProjects / oldboy / 模块 /
/ yitian.py '>, ' sys ': <module ' sys' (built - in )>}

  注意,因为模块在导入的时候会建立其本身的名称空间,因此咱们在使用模块中的变量的时候通常是不会产生冲突的. 

1
2
3
4
5
6
7
8
9
10
11
import  yitian
 
main_person_man  =  "胡一菲"
def  fight_in_shaolin():
  print (main_person_man,  "大战曾小贤" )
 
print (yitian.main_person_man)  # 张无忌
print (main_person_man)  # 胡一菲
 
yitian.fight_in_shaolin()  # 倚天屠龙记中的
fight_in_shaolin()  # 本身的

  注意,在模块中使用global,咱们以前说global表示把全局的内容引入到局部,可是这个全局指的是py文件,换句话说,global指向的是模块内部,并不会改变外部模块的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
模块 yitian 中:
 
print ( "片头曲. 啊! 啊~ 啊! 啊. 啊啊啊啊啊啊啊..." )
 
main_person_man  =  "张无忌"
main_person_woman  =  "赵敏"
 
low_person_man_one  =  "成昆"
low_person_man_two  =  "周芷若"
 
def  fight_on_light_top():
 
     print ( "光明顶大战" , main_person_man,  "破坏了" , low_person_man_one,  "的大阴谋" )
 
def  fight_in_shaolin():
     global  low_person_man_two  # 注意看, 此时的global是当前模块. 并不会影响
其余模块
     low_person_man_two  =  "战五渣"
     print ( "少林寺大战" , main_person_man,  "破坏了" , low_person_man_two,  "的大阴谋" )
 
调用方:
import  yitian
 
low_person_man_two  =  "刘海柱"
 
yitian.fight_in_shaolin()
 
print (yitian.low_person_man_two)  # 战五渣
 
print (low_person_man_two)  # 刘海柱. 并无改变当前模块中的内容. 因此模块内部的
global 只是用于模块内部

  特别特别要注意,若是咱们在不一样的模块中引入了同一个模块,而且在某一个模块中改变了被引入模块中的全局变量,则其余模块看到的值也跟着变,缘由是python的模块只会引入一次,你们共享同一个名称空间

1
2
3
4
5
6
7
8
9
金庸:
import  yitian
yitian.main_person_man  =  "灭绝师太"
 
金庸二号:
import  yitian
import  金庸
 
print (yitian.main_person_man)  # 灭绝师太.

  上述问题出现的缘由: 

    1. 你们共享同一个模块的名称空间.

    2. 在金庸里改变了主⻆的名字

  如何解决呢?

    首先, 咱们不能去改python,由于python的规则不是咱们定的,只能想办法不要改变主⻆的名字,可是在金庸里我就有这样的需求,那此时就出现了,在金庸被执行的时候要执行的代码,在金庸被别人导入的时候咱们不想执行这些代码,此时咱们就要利用一下__name__这个内置变量了. 在Python中,每一个模块都有本身的__name__ ,可是这个__name__的值是不定的,当咱们把一个模块做为程序运行的入口时,此时该模块的__name__是"__main__" , 而若是咱们把模块导入时,此时模块内部的__name__就是该模块自身的名字

1
2
3
4
5
6
7
金庸:
print (__name__)
# 此时若是运行该文件,则__name__是__main__
 
金庸二号:
import  金庸
#此时打印的结果是"金庸"

  咱们能够利用这个特性来控制模块内哪些代码是在被加载的时候就运行的,哪些是在模块被别人导入的时候就要执行的,也能够屏蔽掉一些不但愿别人导入就运行的代码,尤为是测试代码. 

1
2
3
4
5
if  __name__  = =  '__main__' :
     yitian.main_person_man  =  "灭绝师太"  # 此时, 只有从该模块做为入口运行的时候才
会把main_person_man设置成灭绝师太
     print ( "哇哈哈哈哈哈" # 只有运行该模块才会打印,import的时候是不会执行这里的代
码的

  咱们还能够对导入的模块进行从新命名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import  yitian as yt  # 导入yitian. 可是名字被从新命名成了yt. 就比如变量赋值同样.
=  1  =  a
 
yt.fight_in_shaolin()  # 此时能够正常运行
# yitian.fight_in_shaolin() # 此时程序报错. 由于引入的yitian被重命名成了yt
 
print ( globals ())
{ '__name__' '__main__' '__doc__' None '__package__' None ,
'__loader__' : <_frozen_importlib_external.SourceFileLoader  object  at
0x103209438 >,  '__spec__' None '__annotations__' : {},   '__builtins__' :
<module  'builtins'  (built - in )>,  '__file__' :
'/Users/sylar/PycharmProjects/oldboy/模块/模块/金庸.py' '__cached__' None ,
'yt' : <module  'yitian'  from  ' / Users / sylar / PycharmProjects / oldboy / 模块 /
/ yitian.py'>}

  一次能够引入多个模块

1
import  time, random, json, yitian

  正确的导入模块的顺序: 

    1. 全部的模块导入都要写在最上面,这是最基本的

    2. 先引入内置模块

    3. 再引入扩展模块

    4. 最后引入你本身定义的模块

三. from xxx import xxx

  第一大块关于import就说这么多,接下来咱们来看from xxx import xxx这种导入模块的效果,在使用from的时候, python也会给咱们的模块建立名称空间,这一点和import是同样的,可是from xxx import xxx的时候,咱们是把这个空间中的一些变量引入过来了,说白了就是部分导入,当一个模块中的内容过多的时候,咱们能够选择性的导入要使用的内容.

1
2
from  yitian  import  fight_in_shaolin
fight_in_shaolin()

  此时是能够正常运行的,可是咱们省略了以前的模块.函数() 直接函数()就能够执行了, 而且from语句也支持一行语句导入多个内容

1
2
3
4
from  yitian  import  fight_in_shaolin, fight_on_light_top, main_person_man
fight_in_shaolin()
fight_on_light_top()
print (main_person_man)

  一样支持as

1
2
3
4
5
from  yitian  import  fight_in_shaolin, fight_on_light_top, main_person_man as
big_lao
fight_in_shaolin()
fight_on_light_top()
print (big_lao)

  最后看一下from的坑,当咱们从一个模块中引入一个变量的时候,若是当前文件中出现了重名的变量时,会覆盖掉模块引入的那个变量. 

1
2
3
from  yitian  import  main_person_man
main_person_man  =  "超级大灭绝"
print (main_person_man)

  因此,不要重名,切记,不要重名! 不只仅是变量名不要重复,咱们本身建立的py文件的名字不要和系统内置的模块重名,不然引入的模块都是python内置的模块. 切记, 切记.

  咱们如今知道可使用import和from xxx import xxx来导入一个模块中的内容,那有一种特殊的写法: from xxx import * 咱们说此时是把模块中的全部内容都导入, 注意,若是模块中没有写出__all__ 则默认全部内容都导入,若是写了__all__ 此时导入的内容就是在__all__列表中列出来的全部名字.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# haha.py
 
__all__  =  [ "money" "chi" ]
money  =  100
 
def  chi():
  print ( "我是吃" )
 
def  he():
  print ( "我是呵呵" )
 
# test.py
 
from  haha  import  *
 
chi()
print (money)
# he() # 报错

四.包

  包是一种经过 '.模块名'来组织python模块名称空间的方式,那什么样的东西是包呢? 咱们建立的每一个文件夹均可以被称之为包,可是咱们要注意, 在python2中规定,包内必须存在__init__.py文件,建立包的目的不是为了运行, 而是被导入使用,包只是一种形式而已,包的本质就是一种模块

  为什么要使用包? 包的本质就是一个文件夹, 那么文件夹惟一的功能就是将文件组织起来,随着功能越写越多, 咱们没法将全部功能都放在一个文件中, 因而咱们使用模块去组织功能随着模块愈来愈多, 咱们就须要用文件夹将模块文件组织起来, 以此来提升程序的结构性和可维护性

  首先, 咱们先建立一些包,用来做为接下来的学习,包很好建立,只要是一个文件夹, 有__init__.py就能够

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import  os
os.makedirs( 'glance/api' )
os.makedirs( 'glance/cmd' )
os.makedirs( 'glance/db' )
=  []
l.append( open ( 'glance/__init__.py' , 'w' ))
l.append( open ( 'glance/api/__init__.py' , 'w' ))
l.append( open ( 'glance/api/policy.py' , 'w' ))
l.append( open ( 'glance/api/versions.py' , 'w' ))
l.append( open ( 'glance/cmd/__init__.py' , 'w' ))
l.append( open ( 'glance/cmd/manage.py' , 'w' ))
l.append( open ( 'glance/db/__init__.py' , 'w' ))
l.append( open ( 'glance/db/models.py' , 'w' ))
map ( lambda  f:f.close() ,l)

  建立好目录结构

  咱们接下来给每一个文件中添加一些方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#policy.py
def  get():
     print ( 'from policy.py' )
 
#versions.py
def  create_resource(conf):
     print ( 'from version.py: ' ,conf)
 
#manage.py
def  main():
     print ( 'from manage.py' )
 
#models.py
def  register_models(engine):
     print ( 'from models.py: ' ,engine)

  接下来咱们在test中使用包中的内容,而且咱们导入包的时候可使用import或者from xxx import xxx这种形式. 

  首先, 咱们看import

1
2
import  glance.db.models
glance.db.models.register_models( 'mysql' )

  没问题, 很简单, 咱们还可使用from xxx import xxx 来导入包内的模块

1
2
from  glance.api.policy  import  get
get()

  也很简单, 可是要注意,from xxx import xxx这种形式, import后面不能够出现"点" 也就是说from a.b import c是ok的,可是 from a import b.c 是错误的

  好了, 到目前为止, 简单的包已经可使用了,那包里的__init__.py是什么鬼? 其实不论咱们使用哪一种方式导入一个包, 只要是第一次导入包或者是包的任何其余部分, 都会先执行__init__.py文件,这个文件能够是空的,但也能够存放一些初始化的代码. (随意在glance中的__init__.py均可以进行测试) 

  那咱们以前用的from xxx import *还能够用么? 能够,咱们要在__init__.py文件中给出_all__来肯定* 导入的内容. 

1
2
3
4
5
6
7
8
9
print ( "我是glance的__init__.py文件. " )
=  10
def  hehe():
     print ( "我是呵呵" )
 
def  haha():
     print ( "我是哈哈" )
 
__all__  =  [ 'x' "hehe" ]

  test.py

1
2
3
4
5
from  glance  import  *
 
print (x)  # OK
hehe()  # OK
haha()  # 报错. __all__里没有这个鬼东西

  接下来, 咱们来看一下绝对导入和相对导入, 咱们的最顶级包glance是写给别人用的,而后再glance包内部也会有彼此之间互相导入的需求, 这时候就有绝对导入和相对导入两种方式了. 

    1. 绝对导入: 以glance做为起始

    2. 相对导入: 用. 或者..做为起始

  例如, 咱们在glance/api/version.py中使用glance/cmd/manage.py

1
2
3
4
5
6
7
8
9
10
11
# 在glance/api/version.py
 
#绝对导入
from  glance.cmd  import  manage
manage.main()
 
#相对导入
# 这种情形不能够在versions中启动程序.
# attempted relative import beyond top-level package
from  ..cmd  import  manage
manage.main()

  测试的时候要注意,python包路径跟运行脚本所在的目录有关系,说白了就是你运行的py文件所在的目录,在python中不容许你运行的程序导包的时候超过当前包的范围(相对导入). 若是使用绝对导入,没有这个问题,换个说法,若是你在包内使用了相对导入,那在使用该包内信息的时候,只能在包外面导入.

  接下来咱们来看一个大坑,好比,咱们想在policy中使用verson中的内容. 

1
2
# 在policy.py
import  versions

  若是咱们程序的入口是policy.py 那此时程序是没有任何问题的,可是若是咱们在glance外面import了glance中的policy就会报错 ,缘由是若是在外面访问policy的时候,sys.path中的路径就是外面, 因此根本就不能直接找到versions模块,因此必定会报错:

1
ModuleNotFoundError: No module named  'versions'

  在导包出错的时候,必定要先看sys.path 看一下是否真的能获取到包的信息. 

  最后, 咱们看一下如何单独导入一个包. 

1
2
# 在test.py中
import  glance

  此时导入的glance什么都作不了,由于在glance中的__init__.py中并无关于子包的加载,此时咱们须要在__init__.py中分别去引入子包中的内容. 

    1. 使用绝对路径

    2. 使用相对路径

  包的注意事项: 

    1. 关于包相关的导入语句也分为import和from xxx import xxx两种, 但不管使用哪一种,不管在什么位置, 在导入时都必须遵循一个原则: 凡是在导入时d带点的,点左边都必须是一个包,不然报错,能够带一连串的点, 好比a.b.c

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

    3. 包A和包B下有同名模块也不会冲突, 如A.a和B.a来自两个名称空间

相关文章
相关标签/搜索