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实现在每个独立进程中运行一个函数的方法
Apr 23 Python
Python提取网页中超链接的方法
Sep 18 Python
python3第三方爬虫库BeautifulSoup4安装教程
Jun 19 Python
python实现NB-IoT模块远程控制
Jun 20 Python
使用 Visual Studio Code(VSCode)搭建简单的Python+Django开发环境的方法步骤
Dec 17 Python
Django文件存储 自己定制存储系统解析
Aug 02 Python
python虚拟环境完美部署教程
Aug 06 Python
python生成requirements.txt的两种方法
Sep 18 Python
window环境pip切换国内源(pip安装异常缓慢的问题)
Dec 31 Python
Python批量删除mysql中千万级大量数据的脚本分享
Dec 03 Python
jupyter notebook更换皮肤主题的实现
Jan 07 Python
python中四舍五入的正确打开方式
Jan 18 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
转PHP手册及PHP编程标准
2006/12/17 PHP
laravel项目利用twemproxy部署redis集群的完整步骤
2018/05/11 PHP
php输出反斜杠的实例方法
2019/09/19 PHP
myEvent.js javascript跨浏览器事件框架
2011/10/24 Javascript
jquery星级插件、支持页面中多次使用
2012/03/25 Javascript
输入自动提示搜索提示功能的使用说明:sugggestion.txt
2013/09/02 Javascript
web css实现整站样式互相切换
2013/10/29 Javascript
javascript框架设计读书笔记之数组的扩展与修复
2014/12/02 Javascript
JavaScript数据结构和算法之二叉树详解
2015/02/11 Javascript
javascript结合CSS实现苹果开关按钮特效
2015/04/07 Javascript
jquery实现移动端点击图片查看大图特效
2020/09/11 Javascript
jquery插件之文字间歇自动向上滚动效果代码
2016/02/25 Javascript
基于jQuery实现Ajax验证用户名是否存在实例
2016/03/30 Javascript
javascript和jQuery实现网页实时聊天的ajax长轮询
2016/07/20 Javascript
HTML中使背景图片自适应浏览器大小实例详解
2017/04/06 Javascript
jQuery自定义多选下拉框效果
2017/06/19 jQuery
Vue编写多地区选择组件
2017/08/21 Javascript
JS兼容所有浏览器的DOMContentLoaded事件
2018/01/12 Javascript
[03:43]2014DOTA2西雅图国际邀请赛 newbee战队巡礼
2014/07/07 DOTA
Python对象的深拷贝和浅拷贝详解
2014/08/25 Python
Python实现的下载8000首儿歌的代码分享
2014/11/21 Python
python入门基础之用户输入与模块初认识
2016/11/14 Python
Python中easy_install 和 pip 的安装及使用
2017/06/05 Python
PyTorch的SoftMax交叉熵损失和梯度用法
2020/01/15 Python
canvas实现飞机打怪兽射击小游戏的示例代码
2018/07/09 HTML / CSS
html5 worker 实例(二) 图片变换效果
2013/06/24 HTML / CSS
SEPHORA丝芙兰捷克官网:购买香水、化妆品和护肤品
2018/11/26 全球购物
L’Artisan Parfumeur官网:法国香水品牌
2020/08/11 全球购物
南京软件公司的.net程序员笔试题
2014/08/31 面试题
大学生入党自我鉴定
2013/10/31 职场文书
2014新年元旦活动策划方案
2014/02/18 职场文书
工作说明书范文
2014/05/07 职场文书
班级出游活动计划书
2014/08/15 职场文书
领导班子三严三实心得体会
2014/10/13 职场文书
大学生旷课检讨书1000字
2015/02/19 职场文书
仅用一句SQL更新整张表的涨跌幅、涨跌率的解决方案
2021/05/06 MySQL