黑马程序员技术交流社区

标题: 【上海校区】python模块详解 [打印本页]

作者: 不二晨    时间: 2018-11-12 09:18
标题: 【上海校区】python模块详解
使用python时,常常会涉及到库的调用,这就需要掌握模块的基本知识。本文分为如下几个部分
概念说明
这里厘清python中模块、库、包之间的概念差异
模块的简单调用
比如我们有一个trymodule的文件夹,里面有一个first.py文件,文件中的内容如下
a = 1
def myfun(s):
    print(s + 1)
复制代码
在trymodule的文件夹下打开命令行窗口(按住shift单击鼠标右键,选择“在此处打开命令窗口”),输入python进入命令行交互模式
>>> import first
>>> a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'a' is not defined
>>> first.a
1
>>> first.myfun(2)
3
复制代码
所以说first.py文件就是一个模块,可以用import导入,里面变量都要用first.前缀来引用,如果想不使用这个前缀可以这样
>>> from first import a
>>> a
1
复制代码
其他用法如下
# 重命名
>>> from first import myfun as addone
>>> addone(4)
5
# 导入模块中全部变量
>>> from first import *
>>> myfun(2)
3
# 一次导入多个变量
>>> from first import a, myfun
>>> a
1
复制代码
包的导入
在trymodule文件夹中新建folder1文件夹,我们想让folder1文件夹成为一个包。文件夹里新建abcd.py文件,文件中内容如下
b = 2
class Myclass:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def get_info(self):
        print('my name is {name} and age is {age}'.format(name = self.name, age = self.age))
复制代码
此时在folder1文件夹中新建一个__init__.py文件,否则程序会将这个文件夹当成普通文件夹来处理而不是一个包。这个__init__.py文件中可以什么都不填。
此时文件结构如下
trymodule
│   first.py
├───folder1
│   │   abcd.py
│   │   __init__.py
复制代码
我们还是在trymodule文件夹下打开命令行,进入python交互模式
我们来看一下下面几种导入方式
>>> import folder1
>>> folder1.abcd.b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: module 'folder1' has no attribute 'abcd'
>>> from folder1 import abcd
>>> bob = abcd.Myclass(name = 'Bob', age = 20)
>>> bob.name
'Bob'
>>> bob.get_info()
my name is Bob and age is 20
>>> from folder1.abcd import b
>>> b
2
>>> import folder1.abcd
>>> abcd.b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'abcd' is not defined
>>> folder1.abcd.b
2
>>> import folder1.abcd as aa
>>> aa.b
2
复制代码
注意:
特殊的__init__.py文件
__init__.py文件其实是一个特殊的文件,它相当于名为folder1模块,即如果使用import folder1则可以调用在__init__.py文件文件中定义的变量。
将__init__.py文件编写如下
from folder1.abcd import b
c = 3
复制代码
在trymodule文件夹下打开命令行,进入python交互模式
>>> import folder1
>>> folder1.c
3
>>> folder1.b
2
>>> from folder1 import b
>>> b
2
复制代码
对比之前的from folder1.abcd import b,使用__init__.py文件可以将常用的一些变量导入以方便调用。
另外需要注意两点
导入模块的搜索路径
用import hello时,python会搜寻hello.py文件,搜索顺序如下
>>> import sys
>>> sys.path
['', 'C:\\Program Files\\Anaconda3\\python35.zip', 'C:\\Program Files\\Anaconda3\\DLLs', 'C:\\Program Files\\Anaconda3\\lib', 'C:\\Program Files\\Anaconda3', 'C:\\Program Files\\Anaconda3\\lib\\site-packages', 'C:\\Program Files\\Anaconda3\\lib\\site-packages\\Sphinx-1.4.6-py3.5.egg', 'C:\\Program Files\\Anaconda3\\lib\\site-packages\\snownlp-0.12.3-py3.5.egg', 'C:\\Program Files\\Anaconda3\\lib\\site-packages\\win32', 'C:\\Program Files\\Anaconda3\\lib\\site-packages\\win32\\lib', 'C:\\Program Files\\Anaconda3\\lib\\site-packages\\Pythonwin', 'C:\\Program Files\\Anaconda3\\lib\\site-packages\\setuptools-27.2.0-py3.5.egg']
复制代码
其中第一个''表示当前的工作路径,我们可以看出安装的第三方包所在路径('C:\\Program Files\\Anaconda3\\lib\\site-packages')也在这个列表之中,所以无论工作路径在哪里,都能搜寻到这些包。
如果想添加搜索路径,可以参考这篇文章
__all__
首先要明确,import *的方式无法导入以下划线开头的变量名
__init__.py文件内容更改如下
from folder1.abcd import b
c = 3
_e = 4
复制代码
python交互模式下
>>> from folder1 import *
>>> c
3
>>> _e
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_e' is not defined
复制代码
而如果指定导入是可以的
>>> from folder1 import c
>>> c
3
>>> from folder1 import _e
>>> _e
4
复制代码
如果定义了__all__,则import *就可以导入下划线开头的变量
__init__.py文件内容更改如下
from folder1.abcd import b
__all__ = ['c', '_e']
c = b + 1
_e = 4
复制代码
python交互模式下
>>> from folder1 import *
>>> b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'b' is not defined
>>> c
3
>>> _e
4
复制代码
可见import *只会导入__all__中指定的变量,无论是否以下划线开头。这样限制可以防止import *命令导入太多变量污染命名空间,过滤掉一些中间变量如b
绝对引用与相对引用
python中的import分为绝对引用和相对引用两种。它们之间的差异在于,引用模块时 定位被引用模块位置 的方式不同
在这样的文件结构下
trymodule
│   first.py
├───folder1
│   │   abcd.py
│   │   __init__.py
复制代码
编写__init__.py文件,其中要引用abcd.py文件中的变量
相对引用中,.是指父文件(也有from . import xxx的用法),.xxx是指同一层文件,..xxx则是与父文件夹同级的xxx文件(多一个.表示多往上一层)
一般用哪个呢?
python3之后官方推荐用绝对引用的方式,只有当模块中文件关系非常复杂时相对引用才会有优势。
import运行本质
使用import语句,要明确两件事
(1)执行导入模块命令时,会首先检查待导入的模块是否在当前已有模块之中,如果有则跳过import。因此模块之间相互引用不会导致无限循环。
查看当前已导入模块使用下面方法
import sys
sys.modules
复制代码
得到结果是一个字典,键是模块名,值是文件所在路径
(2)import语句与文件执行
在这样的文件结构下
trymodule
│   first.py
├───folder1
│   │   abcd.py
│   │   __init__.py
复制代码
folder1是一个package,abcd是一个module
(要知道执行了什么,可以在这些文件之中添加print语句,看是否打印出结果)
知道这个执行原理,可以更好理解前面得到的一些结论
更多运行细节可以参考这篇文章
if __name__ == '__main__'
我们经常会在别人的代码中发现if __name__ == '__main__',为了理解它的作用,我们来看下面的例子
在folder1文件夹下新建new.py文件,里面内容为
print(__name__)
复制代码
在folder1文件夹下打开命令行,输入
python new.py
复制代码
返回结果是__main__
在trymodule文件夹下打开命令行,进入python交互模式
>>> from folder1 import new
folder1.new
>>> print(__name__)
__main__
>>> print(new.__name__)
folder1.new
复制代码
上面测试结果说明直接运行文件和import文件是有差异的,差异在于二者的__name__变量不同。__name__变量是一个特殊的变量,每个py文件运行时都会对应一个__name__变量,即使在交互模式下也可以查看这个变量值。
所以if __name__ == '__main__'的作用就很好理解了,即import时不执行下面的代码,只有在直接执行这个文件时才运行之后的代码。这算是一种约定俗成的写法,如果不怕文件被import,可以不用这个。


【转载】
作者:dwzb
链接:https://juejin.im/post/5aa0ba38f265da2395308dbd




作者: 不二晨    时间: 2018-11-14 15:19
~(。≧3≦)ノ⌒☆
作者: 梦缠绕的时候    时间: 2018-11-15 15:02

作者: 魔都黑马少年梦    时间: 2018-11-15 16:30





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2