Python中使用语句导入模块或包的机制研究


Posted in Python onMarch 30, 2015

这篇文章讨论了Python的from <module> import *和from <package> import *,它们怎么执行以及为什么使用这种语法(也许)是一个坏主意。
从一个模块导入全部

from <module> import * means意味着“我希望能访问<module>中我有权限访问的全部名称”。例如以下代码something.py:
 

# something.py
 
public_variable = 42
_private_variable = 141
 
def public_function():
  print("I'm a public function! yay!")
 
def _private_function():
  print("Ain't nobody accessing me from another module...usually")
 
class PublicClass(object):
  pass
 
class _WeirdClass(object):
  pass

在Python解释器中,我们可以执行from something import *,然后看到如下的内容:
 

>>> from something import *
>>> public_variable
42
>>> _private_variable
...
NameError: name '_private_variable' is not defined
>>> public_function()
"I'm a public function! yay!"
>>> _private_function()
...
NameError: name '_private_function' is not defined
>>> c = PublicClass()
>>> c
<something.PublicClass object at ...>
>>> c = _WeirdClass()
...
NameError: name '_WeirdClass' is not defined

from something import *从something中导入了除了以_开头名称外的其他所有名称,按照规范,_开始的名称是私有的所以未被导入。
嗯,不是特别糟!还有什么?

上面没提到__all__是什么。__all__是一个字符串列表,指定了当from <module> import *被使用时,模块(或者如后文会提到的包)中的哪些符号会被导出。如果我们不定义__all__(我们在上面的something.py就没定义),import *默认的导入方式是导入除了下划线(_)开头的所有名称。再说一次,编程惯例上下划线表示一个符号是私有的,不导入是合理的。让我们来看看在something.py中定义我们自己的__all__会发生什么。
 

# something.py
 
__all__ = ['_private_variable', 'PublicClass']
 
# The rest is the same as before
 
public_variable = 42
_private_variable = 141
 
def public_function():
  print("I'm a public function! yay!")
 
def _private_function():
  print("Ain't nobody accessing me from another module...usually")
 
class PublicClass(object):
  pass
 
class _WeirdClass(object):
  pass

现在,我们期望from something import *只会导入_private_variable和PublicClass:
 

>>> from something import *
>>> public_variable
42
>>> _private_variable
...
NameError: name '_private_variable' is not defined
>>> public_function()
"I'm a public function! yay!"
>>> _private_function()
...
NameError: name '_private_function' is not defined
>>> c = PublicClass()
>>> c
<something.PublicClass object at ...>
>>> c = _WeirdClass()
...
NameError: name '_WeirdClass' is not defined

包是怎样的呢?

当从一个包中导入全部时,__all__的做法和模块基本一样,不过它处理的是包中的模块(而不是把模块中的名都导入)。所以当我们使用from <package> import *.时__all__说明了所有需要被导入当前命名空间的模块。

不同之处在于,如果你在一个包的__init__.py里面没有声明__all__,from <package> import *语句不会导入任何东西(这个说法也不全对,正确的说法在此)
但是,这有什么不好?

继续读之前,在你的Python解释器中,执行import this,再读一遍Python之禅(在你孩子每晚睡前也要读给他们)。

    明确比含糊要好。

from <module> import * 是不明确的。它没告诉我们我们正在导入什么或者我们把什么带入当前命名空间了。更好的做法是显式地导入我们需要的全部名称。这种方式下,读者(非常可能是未来的你自己)就不会困惑于你代码中使用的一个变量/方法/类/其他东西是哪儿来的,这也告诉了我们下一点:

    可读性很重要

即使你需要导入很多东西,一个一个显式地导入也更清楚。使用PEP 328:
 

from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
  LEFT, DISABLED, NORMAL, RIDGE, END)

你现在就能明确知道你的命名空间里有什么,使用ctrl+f能很快地告诉你它们是哪儿来的。

同时,你还总是要承担模块/包作者更改list内容(加/减东西)的风险。也就是下面两者之一:

    作者从__all__里删除了一个字符串。如果你的代码使用了那个名字,你的代码就会报出NameError的错误,并且很难发现为什么。
    作者在__all__里加入了很多东西。你也许不需要这些增加的内容,所以你只是让这些你不关心的东西占满了你的命名空间。他们甚至在你不注意的时候会替代其他同名内容。

当然,有时候从模块或者包中导入全部内容是有用的。不过,这么做之前三思。从我的经验来看,这么做通常只是因为懒。

Python 相关文章推荐
Python中的getopt函数使用详解
Jul 28 Python
Python进行数据提取的方法总结
Aug 22 Python
Windows系统下多版本pip的共存问题详解
Oct 10 Python
Python补齐字符串长度的实例
Nov 15 Python
PyGame贪吃蛇的实现代码示例
Nov 21 Python
python实现汽车管理系统
Nov 30 Python
python的继承知识点总结
Dec 10 Python
Python统计一个字符串中每个字符出现了多少次的方法【字符串转换为列表再统计】
May 05 Python
pandas删除行删除列增加行增加列的实现
Jul 06 Python
Python OpenCV调用摄像头检测人脸并截图
Aug 20 Python
Python使用__new__()方法为对象分配内存及返回对象的引用示例
Sep 20 Python
解决keras GAN训练是loss不发生变化,accuracy一直为0.5的问题
Jul 02 Python
优化Python代码使其加快作用域内的查找
Mar 30 #Python
Python中分数的相关使用教程
Mar 30 #Python
Python2.x中str与unicode相关问题的解决方法
Mar 30 #Python
分享一个常用的Python模拟登陆类
Mar 29 #Python
python实现查询IP地址所在地
Mar 29 #Python
python实现定时播放mp3
Mar 29 #Python
Python实现设置windows桌面壁纸代码分享
Mar 28 #Python
You might like
不用iconv库的gb2312与utf-8的互换函数
2006/10/09 PHP
PHP的面试题集,附我的答案和分析(一)
2006/11/19 PHP
将php数组输出html表格的方法
2014/02/24 PHP
PHP 验证身份证是否合法的函数
2017/02/09 PHP
php数据结构之顺序链表与链式线性表示例
2018/01/22 PHP
javascript 数组排序函数
2009/08/20 Javascript
网站导致浏览器崩溃的原因总结(多款浏览器) 推荐
2010/04/15 Javascript
js关闭当前页面(窗口)的几种方式总结
2013/03/05 Javascript
使用正则表达式的格式化与高亮显示json字符串
2014/12/03 Javascript
举例讲解jQuery中可见性过滤选择器的使用
2016/04/18 Javascript
xtemplate node.js 的使用方法实例解析
2016/08/22 Javascript
JS中showModalDialog关闭子窗口刷新主窗口用法详解
2017/03/25 Javascript
ReactJS实现表单的单选多选和反选的示例
2017/10/13 Javascript
js实现敏感词过滤算法及实现逻辑
2018/07/24 Javascript
JS加密插件CryptoJS实现的DES加密示例
2018/08/16 Javascript
JQuery Ajax跨域调用和非跨域调用问题实例分析
2019/04/16 jQuery
微信小程序批量监听输入框对按钮样式进行控制的实现代码
2019/10/12 Javascript
uni-app如何页面传参数的几种方法总结
2020/04/28 Javascript
vscode中的vue项目报错Property ‘xxx‘ does not exist on type ‘CombinedVueInstance<{ readyOnly...Vetur(2339)
2020/09/11 Javascript
[49:56]VG vs Optic 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
Python中操作MySQL入门实例
2015/02/08 Python
Python实现从百度API获取天气的方法
2015/03/11 Python
Python实现视频下载功能
2017/03/14 Python
sublime python3 输入换行不结束的方法
2018/04/19 Python
修改默认的pip版本为对应python2.7的方法
2018/11/06 Python
在Pycharm中修改文件默认打开方式的方法
2019/01/17 Python
python3.6环境安装+pip环境配置教程图文详解
2019/06/20 Python
Django实现whoosh搜索引擎使用jieba分词
2020/04/08 Python
基于Python爬虫采集天气网实时信息
2020/06/05 Python
基于Python组装jmx并调用JMeter实现压力测试
2020/11/03 Python
大众服装店创业计划书范文
2014/01/01 职场文书
安全目标管理责任书
2014/07/25 职场文书
公文写作:工伤事故分析报告怎么写?
2019/11/05 职场文书
java executor包参数处理功能 
2022/02/15 Java/Android
科普 | 业余无线电知识-波段篇
2022/02/18 无线电
ubuntu下常用apt命令介绍
2022/06/05 Servers