一、模块

相信使用过Python编写代码的同学,会经常在文件头看到这样的import …,是的,这就是导入模块的语句,而每一个后缀名为.py的文件都是一个模块。

import jieba
import os 

1. 什么是模块?

  逻辑上来说模块是一组功能的组合;实质上一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。

import加载的模块分为四个通用类别:

a. 使用python编写的代码(.py文件);
b. 已被编译为共享库或DLL的C或C++扩展;
c. 包好一组模块的包
d. 使用C编写并链接到python解释器的内置模块;

如何使用模块?
  想要使用模块,必须先要将模块加载进来,可以通过关键字 import from进行加载;需要注意的是模块和当前文件在不同的命名空间中。

2. 模块的构成

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

二、模块的导入

1、导入整个模块

  比如我们有一个myModule的文件夹,里面有一个first.py文件,文件中的内容如下

a = 1
def myfun(s):
    print(s + 1)

  在myModule的文件夹下打开终端/cmd,输入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

在这里插入图片描述
2、导入特定的函数/变量

  所以说first.py文件就是一个模块,可以用import导入,里面变量和方法都要用first.前缀来引用,如果想不使用这个前缀或是我们只是想要使用模块中的某个函数,就可以只导入该变量或函数。导入方式为:from module_name import function_name。
  如果导入的是变量,就可以直接输入变量名来获得变量的值;如果直接导入的是函数,可以直接使用function_name() 的方式调用函数,无需在函数名前面加上模块名。

# 导入变量
>>> from first import a
>>> a
1
# 导入函数
>>> from first import myfun
>>> myfun(3)
4
# 一次导入多个变量
>>> from first import a,myfun
>>> a
1
>>> myfun(5)
6
# 导入模块中全部变量
>>> from first import *
>>> a
1
>>> myfun(5)
6
>>>

3、使用as给模块指定别名

  可以在后面使用as给函数指定别名。句式如:import module_name as new_name,

>>> import first as f
>>> f.a
1
>>> f.myfun(6)
7

在上述导入函数的基础上,可以在后面用as语句给导入的函数指定别名。句式如:from module_name import function_name as new_function。

>>> from first import myfun as add
>>> add(8)
9

三、模块、包、库的区别

模块(module) 其实就是py文件,里面定义了一些函数、类、变量等。
包(package)多个模块的聚合体形成的文件夹,里面可以是多个py文件,也可以嵌套文件夹。
是参考其他编程语言的说法,是指完成一定功能的代码集合,在python中的形式就是模块和包。

四、包的导入

导入包的本质:导入一个包就是执行包下的__init__.py文件

只要一个文件夹下面有个 init.py 文件,那么这个文件夹就可以看做是一个包

包导入的过程和模块的基本一致,只是导入包的时候会执行此包目录下的 init.py 而不是模块里面的语句了。另外,如果只是单纯的导入包,而包的 init.py 中又没有明确的其他初始化操作,那么此包下面的模块是不会自动导入的。

下面我们来实操进行说明

在myModule文件夹中新建folder文件夹,先cd进入myModule文件夹,在mkdir folder新建folder文件夹

xws@MHQ0063 myModule % mkdir folder

我们想让folder文件夹成为一个包。文件夹里新建abcd.py文件,输入vi 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))

esc–>:wq退出保存
在这里插入图片描述
此时在folder文件夹中新建一个__init__.py文件,否则程序会将这个文件夹当成普通文件夹来处理而不是一个包。这个__init__.py文件中可以什么都不填。

上面步骤操作下来,文件结构如下
在这里插入图片描述
我们还是在myModule文件夹下打开命令行,进入python交互模式

>>> import folder
>>> folder.abcd.b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'abcd'
# 只是导入包不能随便使用其中的模块,要导入到具体模块或者变量的层次

>>> from folder import abcd
>>> bob = abcd.Myclass(name = 'Bob', age = 20)
>>> bob.name
'Bob'
>>> bob.get_info()
my name is Bob and age is 20
>>> from folder.abcd import b
>>> b
2
>>> import folder.abcd
>>> abcd.b
2
>>> import folder.abcd as a
>>> a.b
2

注意:

  • 只是导入包不能随便使用其中的模块,要导入到具体模块或者变量的层次
  • 文件夹与文件之间可以用.也可以用from import格式,而文件与里面的变量之间只能用from import格式,即不能import folder.abcd.b

特殊的__init__.py文件

init.py文件其实是一个特殊的文件,它相当于名为folder模块,即如果使用import folder则可以调用在__init__.py文件文件中定义的变量。

将__init__.py文件编写如下

from folder.abcd import b
c = 3

在myModule文件夹下打开命令行,进入python交互模式

>>> import folder
>>> folder.c
3
>>> folder.b
2
>>> from folder import b
>>> b
2

对比之前的from folder.abcd import b,使用__init__.py文件可以将常用的一些变量导入以方便调用。

另外需要注意两点

  1. __ init__ .py文件编写时,如果要导入其他模块中的变量,即使__ init__.py文件和abcd.py文件在同一个文件夹下,也不能from abcd import b,要从abcd文件从哪里来开始写,即从包的名称开始,from folder.abcd import b。
  2. folder文件夹里的嵌套文件夹内不需要新建__init__.py文件即可像模块一样调用,但是一般还是要新建这个文件,可以方便地导入常用变量。

用import hello时,python会搜寻hello.py文件,搜索顺序如下
先搜寻内置模块是否有hello(因此我们定义的模块名不能内置模块相同)
若内置模块没有,则看以下这些目录里有没有

>>> import sys
>>> sys.path
['', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/Users/xws/Library/Python/2.7/lib/python/site-packages']

其中第一个’‘表示当前的工作路径,我们可以看出安装的第三方包所在路径(‘/Users/xws/Library/Python/2.7/lib/python/site-packages’)也在这个列表之中,所以无论工作路径在哪里,都能搜寻到这些包。建议把模块放在该文件夹下。

__ all __

import *的方式无法导入以下划线开头的变量名_e = 4

__ init__.py文件内容更改如下

from folder.abcd import b
c = 3
_e = 4

python模式下,以下导入方式,在查找变量_e是会报name ‘_e’ is not defined

>>> from folder import *
>>> c
3
>>> _e
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_e' is not defined

但如果指定导入_e便可以

>>> from folder import c
>>> c
3
>>> from folder import _e
>>> _e
4

如果定义了__all__,则import *就可以导入下划线开头的变量

__ init__.py文件内容更改如下

from folder.abcd import b
__all__ = ['c', '_e']
c = b + 1
_e = 4

在这里插入图片描述

>>> from folder 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分为绝对引用和相对引用两种。它们之间的差异在于,引用模块时,定位被引用模块位置 的方式不同。

绝对引用是通过.的连接,指定出最高级文件(夹),到目标文件的绝对路径。我们上面的所有用法都属于绝对引用。

而相对引用是指定待引用模块与当前文件的相对位置,.表示上一级文件

  • 绝对引用:from folder.abcd import myclass
  • 相对引用:from .abcd import myclass

在实际使用中,无论是绝对导入还是相对导入都要注意,如何导入与被调用位置有关。

以下面的文件结构为例
folder1
  │ first1.py
  │ first2.py
  │ init.py
  │
  └─folder2
    second1.py
    second2.py
    init.py

下面分为两种情况

  1. folder1是一个项目。运行这个项目需要在folder文件夹中打开cmd,运行python first1.py

    此时first1.py文件中引用first2.py中变量只需from first2 import sth
    不可以from folder1.first2 import sth,也不可以 from .first2 import sth
    (即使folder1中有定义_init_.py,在文件夹内部打开cmd仍无法搜索到folder1的名称)

    first1.py文件中引用second1.py中变量需要from folder2.second1 import sth
    second1.py文件中引用second2.py中变量需要from folder2.second2 import sth
    或者使用相对引用方法:from .second2 import sth
    (注意first1引用first2 与 second1引用second2 的区别)

  2. folder1是一个包。一般使用时,是在外面的文件夹中import这个包

    此时first1.py文件中引用first2.py中变量需要from folder1.first2 import sth
    或者from .first2 import sth
    不可以 from first2 import sth,即使两个first文件在同一个文件夹内

    从上面两个例子的对比中我们可以看出,一个项目内的文件之间,相互引用变量时,如何import是要看这个项目是在什么文件中调用的,也可以说要看这些文件和调用处之间的相对位置。

import运行本质

使用import语句,要明确两件事

(1)执行导入模块命令时,会首先检查待导入的模块是否在当前已有模块之中,如果有则跳过import。因此模块之间相互引用不会导致无限循环。

查看当前已导入模块使用下面方法

import sys
sys.modules

得到结果是一个字典,键是模块名,值是文件所在路径

(2)import语句与文件执行

在这样的文件结构下

myModule
  | first.py
  |───folder
  |   | abcd.py
  |   | __ init __.py

folder是一个package,abcd是一个module

import folder只是导入package,相当于执行__ init__ .py文件
from folder import abcd则执行了__ init __ .py文件文件与abcd.py文件
from folder.abcd import b其实也执行了 __ init __ . py 文件文件与abcd.py文件
(要知道执行了什么,可以在这些文件之中添加print语句,看是否打印出结果)

知道这个执行原理,可以更好理解前面得到的一些结论

首先是在__init__.py文件中什么都没有的情况下,import folder无法调用abcd模块中的变量,是因为相当与运行了一个空文件,没有将整个包导入工作空间
abcd模块中定义了print语句后,import两次,只有第一次会print出值,说明第二次检查出模块已在导入之列,忽略了这条import命令

六、if __ name__ == '__ main__'有什么作用

我们经常会在代码中看到这一句 if __ name__ == ‘__ main__’ ,为了理解它的作用,我们来看下面的例子

在folder文件夹下新建new.py文件,里面内容为

print(__name__)

在folder文件夹下打开命令行,输入

python new.py

返回结果

xws@MHQ0063 folder % python new.py
__main__

在myModule文件夹下打开命令行,进入python交互模式

>>> from folder import new
folder.new
>>> print(__name__)
__main__
>>> print(new.__name__)
folder.new

在这里插入图片描述
从测试结果可以发现直接运行文件和import文件是有差异的,差异在于二者的__ name__ 变量不同。__ name__ 变量是一个特殊的变量,每个py文件运行时都会对应一个 __ name__变量,即使在交互模式下也可以查看这个变量值。

所以 if __ name__ == '__ main__'的作用就很好理解了,即import时不执行下面的代码,只有在直接执行这个文件时才运行之后的代码这算是一种约定俗成的写法,如果不怕文件被import,可以不用这个。

七、os模块

import os 是导入标准库os
os(operation system)import os
os(Operation System)操作系统

有了OS模块,我们不需要关心什么操作系统下使用什么模块,OS模块会帮你选择正确的模块并调用

参考python模块详解

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐