模块

模块就是完成某项功能的程序集,比如 sys 模块,random 模块等。在 Python 语言中,每个 .py 文件就是一个模块。

创建一个模块

任何以 .py 结尾的文件都是一个模块。
创建 tool.py 文件:

def greeting(name):
    print("Hello, I'm %s"%name)

def add(a,b):
    return a+b

在 py.py 中使用该模块:

import tool
tool.greeting("Mike")

运行结果:

Hello, I'm Mike

引入模块的几种方式

以上面创建的 tool 模块为例,有一下几种引入方式(1 为引入方式,2 为调用方式):

1.import tool
2.tool.greeting("Mike")
1.from tool import greeting,add
2.greeting("Mike")
1.from tool import *
2.greeting("Mike")

__all__ 变量

默认情况下,使用 from xxx import * 导入模块时会导入模块中所有的函数和数据,如果我们想限定哪些内容能被导入,哪些内容不能被导入,就需要使用 __all__ 变量,该变量是系统内置的,默认为一个空列表。我们可以在该列表中添加想要被导出的内容。修改 tool.py:

__all__ = ["greeting"]

def greeting(name):
    print("Hello, I'm %s"%name)

def add(a,b):
    return a+b

在 py.py 中调用 add 方法试试:

from tool import *

greeting("Mike")
print(add(1,2))

运行结果如下:

Hello, I'm Mike
Traceback (most recent call last):
  File "C:\Users\Charley\Desktop\py\py.py", line 4, in <module>
    print(add(1,2))
NameError: name 'add' is not defined

greeting 函数被成功执行了,而 add 函数没有找到。
对于模块内部的私有变量或者函数,还可以在名称前加上一个下划线 _,这样就不会被导出了。
注:这种方式只对 from xxx import * 有效:
tool.py:

_num = 1

def count():
    global _num
    _num += 1
    return _num

py.py:

from tool import *
print(count())
print(_num)

运行结果如下:

2
Traceback (most recent call last):
  File "C:\Users\Charley\Desktop\py\py.py", line 3, in <module>
    print(_num)
NameError: name '_num' is not defined
[Finished in 0.1s with exit code 1]
[shell_cmd: python -u "C:\Users\Charley\Desktop\py\py.py"]

这里无法使用 _num 变量。以上的方式只对 from xxx import * 有效,如果我们不使用星号 * 引入,则无此限制:

from tool import count,_num
print(count())
print(_num)

运行结果为:

2
1

__pycache__ 文件夹

在导入 tool 模块后运行代码,发现多出了一个 __pycache__ 文件夹:

│  py.py
│  tool.py
│
└─__pycache__
        tool.cpython-35.pyc

其中有个 .pyc 文件,这是编译器为了提高运行速度创建的缓存字节码文件,这里有一个简要介绍:

如果 Python 进程在机器上拥有写入权限,那么它将把程序的字节码保存为一个以 .pyc 为扩展名的文件( ".pyc" 就是编译过的 ".py" 源代码)。当程序运行之后,你会在那些源代码的附近(也就是说同一个目录下)看到这些文件。
Python这样保存字节码是作为一种启动速度的优化。下一次运行程序时,如果你在上次保存字节码之后没有修改过源代码的话,Python将会加载.pyc文件并跳过编译这个步骤。当Python必须重编译时,它会自动检查源文件和字节码文件的时间戳:如果你又保存了源代码,下次程序运行时,字节码将自动重新创建。

上面的解释来自于《python学习手册》。
总是,__pycache__ 和其下的 .pyc 文件是解释器为了优化代码执行创建的,并且只会创建被导入的模块缓存,主模块不创建缓存。

有时候一个模块无法完成具体的功能,需要多个模块协作完成,我们可以把这些模块放在一个文件夹中进行统一管理,这就是 Python 中包的概念。
Python3 和 Python2 对于包的概念有点小小的区别:Python3 认为一个文件夹就是一个包,Python2 认为带有 __init__.py 文件的文件夹是一个包。但不管是 Python3 还是 Python3,如果包中有 __init__ 文件,在导入包的时候都会先执行 __init.py__ 文件。
Python3 下可以直接导入包中的模块:

from mypkg import tool
tool.greeting("NAME")

运行结果:

Hello, I'm NAME

Python2 下需要新建 __init__.py 文件:

__all__ = ["tool"]

除此之外使用方式和 Python3 一样:

from mypkg import tool
tool.greeting("Python2 中使用")

为了兼容性,建议总是在包中加上 __init__.py 文件。

模块寻找的路径

sys 模块中的 path 变量可以帮助我们查看 Python 寻找模块的路径:

import sys
print(sys.path)

运行结果:


[
'C:\\Users\\Charley\\Desktop\\py', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\python35.zip', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\DLLs', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib\\site-packages'
]

在寻找模块时,会依次按照列表中的路径寻找,直到找到位置。
如果我们想自定义寻找路径,可以修改 path 变量:

from sys import path
path.append("./mypkg/");

from tool import greeting
greeting("Mike")

运行结果为:

Hello, I'm Mike
[
'C:\\Users\\Charley\\Desktop\\py', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\python35.zip', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\DLLs', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32', 
'C:\\Users\\Charley\\AppData\\Local\\Programs\\Python\\Python35-32\\lib\\site-packages',
'./mypkg/'
]

我们在 path 变量中增加了一条路径,定义到前面建立的包中,这样就可以直接导入包中的模块,不需要导入包名了。这只是一种展示性的做法,不推荐使用。

模块热导入

这是我自己编的一个名词,意思是导入模块后,程序在不退出的前提下,如果修改了被导入的模块,可以同步使用修改后的模块中的功能。
创建一个 tesereload.py 模块:

def show():
    print("Hello World")
    # print("Hello China")

打开命令行,导入 testreload 模块,并执行 show 函数:

>>> import testreload
>>> testreload.show()
Hello World

修改 testreload 模块,取消注释,再次在命令行中执行 show 函数:

>>> testreload.show()
Hello World

在命令行中使用 import 再次导入 testreload 模块,再执行 show 函数:

>>> import testreload
>>> testreload.show()
Hello World

执行结果并没有发生变化,这是因为前面讲到的缓存机制,在程序运行起来之后,修改了原始模块并不会同步更新导入,而是从缓存的 pyc 文件中读取。如果需要重新应用修改后的模块,可以:

  • 重新启动程序
  • 使用 imp 模块中的 reload 方法

继续在命令行中导入 imp 模块,并通过 reload 方法重新载入模块:

>>> from imp import reload
>>> reload(testreload)
<module 'testreload' from 'C:\\Users\\Charley\\Desktop\\py\\testreload.py'>

再次执行 show 方法:

>>> testreload.show()
Hello World
Hello China

可见 show 方法的执行结果已经同步更新了。这就是 reload 的作用。

避免模块循环导入

模块循环导入就是两个模块之间相互依赖导入,会产生死循环,修改 py.py 文件:

from testreload import show

def show2():
    pass
show()

修改 testreload.py 文件:

from py import show2

def show():
    print("Hello World")

show2()

运行 py.py 文件:

Traceback (most recent call last):
  File "C:\Users\Charley\Desktop\py\py.py", line 1, in <module>
    from testreload import show
  File "C:\Users\Charley\Desktop\py\testreload.py", line 1, in <module>
    from py import show2
  File "C:\Users\Charley\Desktop\py\py.py", line 1, in <module>
    from testreload import show
ImportError: cannot import name 'show'
[Finished in 0.2s with exit code 1]
[shell_cmd: python -u "C:\Users\Charley\Desktop\py\py.py"]
[dir: C:\Users\Charley\Desktop\py]
[path: C:\Python27\;C:\Python27\Scripts;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;D:\nvm;D:\nodejs;C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\ProgramData\chocolatey\bin;C:\Program Files (x86)\GtkSharp\2.12\bin;C:\Program Files\MySQL\MySQL Utilities 1.6\;C:\Users\Charley\AppData\Local\Programs\Python\Python35-32\Scripts\;C:\Users\Charley\AppData\Local\Programs\Python\Python35-32\;D:\Git\bin;D:\Sublime Text 3;D:\MinGW\bin\;D:\MinGW\bin\;D:\Microsoft VS Code\bin;D:\Java\jdk 8.0\bin;D:\Android\sdk;C:\Program Files\MySQL\MySQL Server 5.7\bin;]

程序直接挂掉了。因此我们需要避免模块的循环导入,若两个模块相互有依赖,应该通过第三个的抽象进行解决。

__name__ 变量

__name__ 变量是系统提供的一个全局变量,用来对当前的模块进行描述。我们在 py.py 文件和 testreload.py 文件中分别查看 __name__ 变量:
py.py:

import testreload
print("--> py %s"%__name__)

testreload.py:

print("--> testreload %s"%__name__)

运行 py.py 文件:

--> testreload testreload
--> py __main__

__name__ 变量用来对当前的模块进行描述,如果是主模块,其值为 __main__,如果是被导入的模块,其值就是模块名。
实际应用中,我们可以根据 __name__ 变量来决定是否执行模块中的内容。修改 testreload.py:

print("--> testreload %s"%__name__)

if __name__ == "__main__":
    print("__MAIN__")

运行 py.py 文件:

--> testreload testreload
--> py __main__

运行 testreload.py 文件:

--> testreload __main__
__MAIN__

在 testreload 模块中,只有该模块是组模块中才执行 print 函数输出。
项目中我们一般有一个入口文件,建议在入口文件中对 __name__ 进行判断,完成初始化工作:

# 如果当前模块是主模块
if __name__ == "__main__":
    # 执行初始化函数
    main()

完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • 用 python 解释器来编程从 Python 解释器退出再进入,那么你定义的所有的方法和变量就都消失了。 为此...
    chen_000阅读 521评论 0 3
  • 模块简介 在软件开发过程中,随着代码的不断增加,在一个问价里代码就会越来越长,不容易维护。为了编写可维护的代码,我...
    齐天大圣李圣杰阅读 794评论 0 0
  • 模块是最高级别的程序组织单元,它将程序代码和数据封装起来以便重用。 模块是变量名的封装,被认为是命名空间。模块导入...
    lakerszhy阅读 517评论 0 2
  • 课程概要:1、认识Python 模块2、字节编译3、from … import 详解4、认识 name 属性5、自...
    LuCh1Monster阅读 762评论 0 7
  • 目录: 上一节课生成器还有一些知识点没讲到,接下来补充; 一、协程函数 生成器:yield关键字的另外一种用法yi...
    CaiGuangyin阅读 576评论 0 1