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映射列表实例分析
Jan 26 Python
python爬虫框架talonspider简单介绍
Jun 09 Python
Python使用cx_Oracle模块操作Oracle数据库详解
May 07 Python
Numpy数据类型转换astype,dtype的方法
Jun 09 Python
python3 unicode列表转换为中文的实例
Oct 26 Python
Python 从一个文件中调用另一个文件的类方法
Jan 10 Python
Python设计模式之职责链模式原理与用法实例分析
Jan 11 Python
python学生管理系统开发
Jan 30 Python
Python格式化字符串f-string概览(小结)
Jun 18 Python
Expected conditions模块使用方法汇总代码解析
Aug 13 Python
Python自动化之批量处理工作簿和工作表
Jun 03 Python
pandas进行数据输入和输出的方法详解
Mar 23 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:风雨欲来 路在何方?
2006/10/09 PHP
thinkphp特殊标签用法概述
2014/11/24 PHP
YII使用url组件美化管理的方法
2015/12/28 PHP
解读PHP的Yii框架中请求与响应的处理流程
2016/03/17 PHP
修改Laravel5.3中的路由文件与路径
2016/08/10 PHP
浅谈php数组array_change_key_case() 函数和array_chunk()函数
2016/10/22 PHP
THINKPHP5.1 Config的配置与获取详解
2020/06/08 PHP
jQuery EasyUI API 中文文档 - Menu菜单
2011/10/03 Javascript
jQuery 三击事件实现代码
2013/09/11 Javascript
简述JavaScript的正则表达式中test()方法的使用
2015/06/16 Javascript
浅析Bootstrip的select控件绑定数据的问题
2016/05/10 Javascript
js实现可输入可选择的select下拉框
2016/12/21 Javascript
基于rem的移动端响应式适配方案(详解)
2017/07/07 Javascript
微信小程序 获取session_key和openid的实例
2017/08/17 Javascript
vue 动态改变静态图片以及请求网络图片的实现方法
2018/02/07 Javascript
vuejs+element UI点击编辑表格某一行时获取内容填入表单的示例
2018/10/31 Javascript
Vant Weapp组件踩坑:picker的初始赋值解决
2020/11/12 Javascript
[55:44]OG vs NAVI 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
使用python3+xlrd解析Excel的实例
2018/05/04 Python
pycharm下查看python的变量类型和变量内容的方法
2018/06/26 Python
使用Python的Dataframe取两列时间值相差一年的所有行方法
2018/07/10 Python
在Python中表示一个对象的方法
2019/06/25 Python
python统计字符的个数代码实例
2020/02/07 Python
移动端html5 meta标签的神奇功效
2016/01/06 HTML / CSS
西班牙美妆电商:Perfume’s Club(有中文站)
2018/08/08 全球购物
ZINVO手表官网:男士和女士手表
2019/03/10 全球购物
澳大利亚排名第一的露营和户外设备在线零售商:Outbax
2020/05/06 全球购物
会计专业毕业生求职信分享
2014/01/03 职场文书
学生干部学习的自我评价
2014/02/18 职场文书
家居装修公司创业计划书范文
2014/03/20 职场文书
公司经理任命书
2014/06/05 职场文书
文明工地标语
2014/06/16 职场文书
医学生自荐信范文
2015/03/05 职场文书
科技馆观后感
2015/06/08 职场文书
健身房被搭讪?用python写了个小米计时器助人为乐
2021/06/08 Python
英镑符号 £
2022/02/17 杂记