HOME/Articles/

Python中的模块

Article Outline

Python中的模块

<!--more-->

一个py文件就是一个模块

模块使用举例:

import os

print(os.__file__) # /usr/lib/python3.5/os.py

上面的import os就使用了一个内置模块os,通过os.__file__可以获得本地这个模块的路径

包管理工具pip

类似于Node中的npm,不多赘述,直接来看看使用。

直接sudo pip3 install pygame

安装好了模块之后,运行:

import os
import pygame

print(os.__file__) # /usr/lib/python3.5/os.py
print(pygame.__file__) # /usr/local/lib/python3.5/dist-packages/pygame/__init__.py

实现一个本地模块及模块的引用

先在当前目录下创建一个新的模块testModule.py

# testModule.py
def test1():
    print('----test1----')

对testModule模块进行引用

# import testModule

# testModule.test1() # ----test1----

from testModule import test1

test1()

有上述两种方式。

对于模块的查找,也是优先当前目录,然后才去系统的文件夹下寻找模块

模块中的__name____all__

__name__

先来看__name__的用法:

修改testModule.py:

def test1():
    print('----test1----')


print(__name__) 

当通过python3.5 testModule.py时,输出__main__

我们现在在其他文件中引入这个testModule,然后调用test1():

import testModule

testModule.test1()

此时输出的__name__为testModule

所以:

  • 当文件是被直接执行时,__name__的值为__main__
  • 而当模块是被调用时,__name__为模块名

__all__

__all__是针对from moduleName import *这种模块导入方式的:

当我们写下from moduleName import *,这个模块的所有全局变量,函数或者类,都可以被访问。为了自定义模块的输出,可以使用__all__来规定想输出的函数,变量或者类。

来看例子: 我们有如下模块


class Test(object):
    def sayHi(self):
        print('class Test')


def test1():
    print('---test1---')

num = 100

当在其他模块中引入时:

from testModule import *

t = Test()
t.sayHi()

test1()

print(num)
# 输出
# class Test
# ---test1---
# 100

所有的类,函数,全局变量都可以被访问。

现在我们在testModule.py中设置__all__

__all__ = ['Test','test1']

class Test(object):
    def sayHi(self):
        print('class Test')


def test1():
    print('---test1---')

num = 100

我们在__all__中指定了想要暴露出来的东西,Test这个类和test1这个函数

此时运行结果为:

from testModule import *

t = Test()
t.sayHi() # class Test

test1() # ---test1---

print(num) # NameError: name 'num' is not defined

此时num就访问不到了,这就是__all__的作用。

__init__.py

假设我们现在有sendMsg.py和recvMsg.py两个模块,因为这两个模块在功能上有一定的联系,所以我们可以创建一个新的文件夹,把这两个模块放进去,这个新的文件夹可以当做这2个模块的集合。

但是这样有个问题,需要我们在创建的文件夹中新创建一个__init__.py,否则py2中不允许导入(py3允许,但是也使用不了)

我们现在的文件结构是这样的:

├── Msg
│   ├── recvMsg.py
│   └── sendMsg.py

这个Msg就是我们创建好的集合,现在创建__init__.py

├── Msg
│   ├── __init__.py
│   ├── recvMsg.py
│   └── sendMsg.py

现在这个Msg就变成了一个包,然后我们可以在__init__.py中定义__all__变量来控制想暴露的模块:

# __init__.py
__all__ = ['sendMsg','recvMsg']

我们可以在外部:

from Msg import *

sendMsg.test1() # ----sendMsg----
recvMsg.test1() # ----recvMsg-----

另外,在导入Msg这个包的时候,会执行__init__.py这个文件,所以我们可以在内部引入包中的模块,比如sendMsg和recvMsg。

但是由于Python2和Python3在写法上有差异,在这给出一种通用的写法:

# __init__.py
# -*- coding:utf-8 -*-  

__all__ = ['sendMsg','recvMsg']

# import sendMsg,recvMsg # Py2写法 Py3报错
from . import sendMsg,recvMsg

模块的发布

先来准备一个文件夹,里面放入我们写好的包

.
├── prepare2Publish
│   ├── __init__.py
│   └── sendHello.py
└── setup.py

其中prepare2Publish就是我们想发布的包,我们要在setup.py中设置包的各种信息:

# setup.py

from distutils.core import setup

setup(
    name='DeeJay',
    version='1.0',
    description='a test module',
    author='DeeJay',
    py_modules=['prepare2Publish.sendHello']
)

然后要输入命令:python3 setup.py build来构建(python setup.py build为构建py2的包):

build之后,现在目录结构为:

.
├── build
│   └── lib
│       └── prepare2Publish
│           ├── __init__.py
│           └── sendHello.py
├── prepare2Publish
│   ├── __init__.py
│   └── sendHello.py
└── setup.py

然后输入:python3 setup.py sdist进行压缩

压缩之后,目录结构为:

.
├── build
│   └── lib
│       └── prepare2Publish
│           ├── __init__.py
│           └── sendHello.py
├── dist
│   └── DeeJay-1.0.tar.gz
├── MANIFEST
├── prepare2Publish
│   ├── __init__.py
│   └── sendHello.py
└── setup.py

打成的这个压缩包就是我们最终的模块,可以拿来给别人使用。

解压之后可以通过python3 setup.py install就可以安装到本地的系统包里

另外如果我们想发布到pip官网上,可以使用twine,在~路径下配置好自己的.pypirc, 就可以将自己的压缩包发布到pip官网了

import时模块文件的查询路径 sys.path


import sys

print( sys.path )

# 输出:
[
    '/home/deejay/learn-python', 
    '/usr/lib/python35.zip', 
    '/usr/lib/python3.5', 
    '/usr/lib/python3.5/plat-x86_64-linux-gnu', 
    '/usr/lib/python3.5/lib-dynload', 
    '/home/deejay/.local/lib/python3.5/site-packages', 
    '/usr/local/lib/python3.5/dist-packages', 
    '/usr/lib/python3/dist-packages'
]

sys.path是一个列表,其中每一个元素都是一个路径,第一个元素为当前目录下的路径

当我们导入一个模块的时候,程序会依次从sys.path中去寻找,如果最后没找到就抛出异常

此外我们可以通过给sys.path append()一个自定义的路径,让程序去指定目录寻找模块

例如:

    sys.path.append('/home')

模块的重新导入

重新导入指的是,在当前程序中导入了一个目标模块,然后在程序没有退出的情况下,修改了目标模块的代码。

此时在当前程序中,是不会更新模块的,就算再次import也无法更新.

这个时候,我们可以借助imp模块中的reload()方法来进行不退出程序并且可以更新模块的操作:

from imp import *

reload(targetModule)

模块的循环导入

循环导入即: 在a模块中导入b,同时在b模块中导入a。

# a.py

import b

def test():
    print('----a----')
    b.test()

test()
# b.py

import a

def test():
    print('----b----')
    a.test()

test()

对于这种情况,一个比较好的解决方法是:通过一个主模块来引入所有子模块,子模块中避免相互引入:

比如我们现在新增一个main.py:


# main.py

import a
import b

a.test()
b.test()