注册 登录

Python模块导入方式总结

未分类 Sxx 92次浏览 5079字 0个评论

Python模块导入方式总结

Python的包或模块的导入机制是很灵活的,在本文为此做了个总结。


包和模块

通俗来讲,一个包类似一个文件夹,而一个模块就是一个文件。一个包里面会包含多个模块,而模块里面会有类、函数、变量等内容。

包和模块的目的是大大提高了它的可复用性、低耦合性,并且具备特定功能的代码

常规导入

  • 最常使用的导入方式:
import sys  //导入模块或包

使用“import”指定你所想要导入的模块或包即可。

 

  • 当你想要一次性导入多个包或者模块的时:
import os, sys, time

此方式便捷,但违背了Python风格指南——建议将每个导入语句单独成行

 

  • 为了方便记忆,可以在导入模块时,重命名这个模块,并按照从前的方式调用模块即可:
import sys as system
print(system.platform)

 

  • 当然,有些子模块必须要使用点标记法才能导入:
import urllib.error

 

使用from语句导入

  • 当你想要导入一个模块或者库中的某个部分:
from functools import lru_cache

以上代码可以直接调用“lru_cache”。

 

  • 如果按照常规方式导入“functools”,则需要以下方式调用“lru_cache”:
functools.lru_cache(*args)

推荐使用常规方式导入,增加代码可读性,并且更清楚该函数是在哪导入的

 

  • from”还可以导入模块的全部内容:
from os import *

但此方式不推荐使用,会打乱你的命名空间。假设你自己定义了一个与导入模块中名称相同的变量或者函数,在调用该函数时可能使用自定义的内容,在某种程度上会出现逻辑错误,麻烦点,可能还找不到原因。

 

标准库中唯一推荐的全盘导入模块只有Tkinter

 

  • 当你要写自己的模块或包时,建议你在“__init__.py”(两个短下划线)中导入所有内容,让模块或者包使用起来更加方便。但请不要删除“__init__.py”,一旦删除,你的模块或包就不再是模块或包,会变成一个文件夹

 

  • 一个包导入多个项的多种方式:

1.分开且少量导入函数,相对于一行一函数的导入便利许多,并且增加可读性:

from os import path, walk, unlink

from os import uname, remove

2.一次性导入多个项,但需要加上圆括号包含起来:

from os import (path, walk, unlink, uname,
                remove, rename)

3.一次性导入多个项,使用“\”续行符,告诉解释器这行代码延续至下一行

from os import path, walk, unlink, uname, \
                remove, rename

 

相对导入

  • 使用“.”来决定如何相对导入其他包或者模块。“.”代表当前模块,“..”代表上一层模块,“...”代表上上一层模块

使用相对导入模块目的是避免偶然情况下导入标准库中的模块而产生冲突

以下示例来解释下相对导入的工作原理:

my_package/
  __init__.py
  subpackage1/
    __init__.py
    module_x.py
    module_y.py

  subpackage2/
    __init__.py
    module_z.py
  module_a.py

在本地磁盘上找个地方穿件创建上述文件和文件夹。并在最顶层的“__init__.py”中输入以下代码:

from . import subpackage1

from . import subpackage2

进入到“subpackage1”文件夹中,编辑里面的“__init__.py”文件,输入以下代码:

from . import module_x

from . import module_y

编辑“module_x.py”文件,输入以下代码:

from .module_y import spam as ham

def main():
    ham()

最后编辑“module_y.py”文件,输入以下代码:

def spam():
    print('spam ' * 3)

打开终端,进入到“my_package”所在的文件夹,运行Python解释器:

In [1]: import my_package

In [2]: my_package.subpackage1.module_x
Out[2]: <module 'my_package.subpackage1.module_x' from 'my_package/subpackage1/module_x.py'>

In [3]: my_package.subpackage1.module_x.main()
spam spam spam

相对导入适用于你最终要放入包中的代码。如果编写了许多相关性强的代码,应采取相对导入的方式。(PYPI上有许多流行的包也是采用了相对导入的方式。)若想跨越多个文件层级进行导入,只需使用多个“.”,不过建议相对导入的层级不要超过两层

 

若往“module_x.py”文件中添加“if__name__==’__main__’”,运行这个文件,会出现:

from . module_y import spam as ham

def main():
    ham()

if __name__ == '__main__':
    # This won't work!
    main()

从终端进入“subpackage1”文件夹,运行一下命令:

python module_x.py

会有两种情况的错误,若使用的是Python 2

Traceback (most recent call last):
    File "module_x.py", line 1, in <module>
        from . module_y import spam as ham
ValueError: Attempted relative import in non-package

若使用的是Python 3

Traceback (most recent call last):
    File "module_x.py", line 1, in <module>
        from . module_y import spam as ham
SystemError: Parent module '' not loaded, cannot perform relative import

module_x.py”是某包中的一个模块,如果试图以脚本模式执行,此模式不支持相对导入

 

若果想使用这个模块,需要将其添加到Python的导入检索路径(import search path):

import sys
sys.path.append('/path/to/folder/containing/my_package')
import my_package
注意点:

添加的是“my_package”的上一层文件夹路径不是“my_package”本身。因为“my_package”是我们想要使用的包,如果添加它的路径,则无法使用这个包

 

可选导入

  • 可选导入就像是你的备用选项。如果你想优先使用某个模块或包,但有担心如果没有这个模块或包的情况下有备选,那你就可以使用可选导入方式。这样可以导入支持某个软件的多种版本或实现性能提升。以github2包中的代码为例:
try:
  # For Python 3
  from http.client import responses
except ImportError:  # For Python 2.5-2.7
  try:
    from httplib import responses  # NOQA
  except ImportError:  # For Python 2.4
    from BaseHTTPServer import BaseHTTPRequestHandler as _BHRH
    responses = dict([(k, v[0]) for k, v in _BHRH.responses.items()])

lxml”包也有使用可选导入方式:

try:
  from urlparse import urljoin
  from urllib2 import urlopen
except ImportError:
  # Python 3
  from urllib.parse import urljoin
  from urllib.request import urlopen

如以上所示,可选导入的使用很常见,是个值得掌握的技巧

 

局部导入

  • 当你在局部作用域中导入模块时,你执行的就是局部导入。如果你在Python脚本文件的顶部导入一个模块,那么你就是在将该模块导入至全局作用域,这意味着之后的任何函数或方法都可能访问该模块。例如:
import sys  # global scope

def square_root(a):
  # This import is into the square_root functions local scope
  import math
  return math.sqrt(a)

def my_pow(base_num, power):
  return math.pow(base_num, power)

if __name__ == '__main__':
  print(square_root(49))
  print(my_pow(2, 3))

以上例子,我们将“sys”模块导入至全局作用域 ,但并没有使用。在“square_root”函数中,将“math”模块导入至该函数的局部作用域,意味着“math”模块只能在“square_root”函数内部使用。在下一函数“my_pow”中便不能使用“math”,会引发“NameError”。

 

  • 使用局部作用域的好处是,如果你是用的模块导入的时间较长,可以将它放在不经常调用的函数中,减少整体运行的时间。但因为Python标准,所有的导入语句都应该位于模块的顶部,因此局部调用模块的情况较少。

 

注意事项

循环导入(circular imports)

  • 在创建两个模块是,如果二者相互导入对方,便会出现循环导入
# a.py
import b

def a_test():
  print("in a_test")
  b.b_test()

a_test()

在同一文件夹中创建另一个模块,命名为“b.py”:

import a

def b_test():
  print('In test_b"')
  a.a_test()

b_test()

如果试图运行任意一个模块,都会引发“AttributeError”,因为两个模块都在试图导入对方。解决办法是重构代码

 

覆盖导入(shadowed imports)

当你创建的模块与标准库中的模块同名时,在导入模块时会出现覆盖导入

假设你创建了一个名为“math.py”的文件,代码如下:

import math

def square_root(number):
  return math.sqrt(number)
square_root(72)

打开终端,运行该文件时,得到以下回溯信息(traceback)

Traceback (most recent call last):
  File "math.py", line 1, in <module>
    import math
  File "/Users/michael/Desktop/math.py", line 6, in <module>
    square_root(72)
  File "/Users/michael/Desktop/math.py", line 4, in square_root
    return math.sqrt(number)
AttributeError: module 'math' has no attribute 'sqrt'

原因是,在运行文件时,Python解释器首先在当前运行脚本所处的文件夹中查找名为“math”的模块。在此例子中,解释器知道了正在执行的模块,试图导入它。但我们的模块中没有叫“sqrt”的函数或属性,因此出现个错误(AttributeError)。

 

总结

  • 在导入模块的同时,尽量根据Python风格指南来敲代码,在增加代码可读性的同时,还可以形成良好整洁的代码风格
  • Python中有许多模块值得学习,多看看别人的代码,根据自己的经验总结出适合自己的写代码技巧等。

 

参考:

Python导入模块的几种姿势

Python模块与包

Python模块包中__init__.py文件的作用

 


T1op|T1op.com , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明Python模块导入方式总结
喜欢 (1)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址