你写了一个程序:
from some_module import * print("Hello World!")
怎么运行都没有打印结果,然后打开some_module.py
一看
def print(*args): exit(1)
你气不气?
这就算善良的,它要给你写个os.remove()
之类的,你不更气?
当然了这是个很极端的情况,真的有恶意代码也不是不用from X import *
就能解决的。这种写法的核心问题就是命名空间的冲突,因为你根本不知道你import
进来了啥。
写程序这个事情,几乎在任何情况下,白名单都优于黑名单。白名单是可控的,黑名单是总会有漏网之鱼的。当你去拿车库拿工具的时候,需要锤子就拿锤子,需要锯子就拿锯子,不要把那个巨大的工具箱直接拿来,因为里面可能藏着爸爸拿来挖矿的炸药,见点火星子你就上天了。
在使用库的时候,基本上没有人对这个库是100%熟悉的,那你就只引入你想用的东西,显式地引入,这样可以在完全不了解这个库的情况下发现命名冲突的问题。如果你做了import *
,那很可能会有大量的你并不熟悉甚至完全不知道的内容被引入,然后发生冲突,产生非常诡异的bug,严重浪费你的时间。
我不同意只要语言允许的,就应该去使用;就像我不同意家里存在的东西,小孩子就能玩一样。在使用任何一门语言的时候,都应该尽量选择这个语言允许范围内的效率更高、错误率更低的写法,这才是一个合格的程序员。
谢邀。
大家都讲得很明白了,from xxx import * 这种方法可能造成命名冲突。
例如A是卖数码产品的,B是卖水果的。A和B服务于同一个老板。他们都各有一个行李箱,现在他们都在老板面前,给老板供货。现在有两种情况:
现在A和B箱子里都有一个iPhone包装盒(假设是同一款iphone)
A的iPhone盒子里面,装了个手机
B的iPhone盒子里,装了个红富士苹果(别问B为啥这么做,问就是创意)
如果A和B的行李箱物品都倒出来了(from A import * , from B import *),你能分辨出哪个盒子装的是手机,哪个盒子装的是红富士吗?
from A import * from B import * use(apple)
但如果需要时再拿出来,我们就可以清楚知道,要红富士,直接去B的行李箱拿(from B import apple)
from B import apple use(apple)
如果包装盒不打开,仅凭盒子外观,就很难分辨哪个apple是iphone,哪个apple是苹果。
我顺便来告诉大家为啥是「无论什么情况下」都不应该用这种方法。因为破窗效应。
以一幢有少许破窗的建筑为例,如果那些窗不被修理好,可能将会有破坏者破坏更多的窗户。最终他们甚至会闯入建筑内,如果发现无人居住,也许就在那里定居或者纵火。一面墙,如果出现一些涂鸦没有被清洗掉,很快的,墙上就布满了乱七八糟、不堪入目的东西
此理论认为环境中的不良现象如果被放任存在,会诱使人们仿效,甚至变本加厉。对于小工程还好,对于大的软件工程,如果你没有良好的代码规范,那么失控是非常容易的事。这也是为啥很多人抱怨在大公司做事没啥自由度,但事情做起来成功率要高。
你也许很清楚你的模块里面都有什么函数,你大胆使用from your_own_module import *,但你的代码不仅仅属于你,你的代码属于整个团队。当团队其他成员看到你这么写,那他很容易跟着模仿,毕竟项目代码风格这个东西,不就是团队之间互相配合出来的吗?你总不能写完这个代码后,告诉大家「虽然我用了import *,但这是我的特权,你们不能学我」吧?
所以,倒不如直接一刀切,制定好代码规范。让人自律很难,但让人遵守规则,则容易得多
可以抽象一点看代码
import numpy
可以理解为在第一层创建了一个文件夹,
main.py def function1() def function2() numpy |- numpy.random() |- numpy.exp()
函数放在了第二层文件夹里,
所以调用时要用完整的函数名定向的指向。
numpy.random()
如果
from numpy import *
就会成为
main.py def function1() def function2() np.random() np.exp()
当你自定义的函数或者几个库的函数重名,
就会把先前的函数覆盖掉,
特别当大型工程时,调用的库复杂繁多,灾难性的后果......
引申的操作就是:
当你的工程文件长这样
- project |- file1 |- test1.py |- file2 |- test2.py
想在 test1.py 用 test2.py 里面的东西
可以用
import sys sys.path.append('..') from file2 import test2.py
就像先访问本文件上层的文件夹,再 import 同层的文件.
所以一般最好就写成
import numpy as np import torch from torchvision import datasets, transforms
这三种写法比较易读,而且记不住调用情况的时候可以随时搜索找到 import 的地方。
这个问题本质很简单:因为给孩子起名怕重名,写程序同样也怕重名。
几乎所有的现代编程语言都采用这种方法避免重名:用完整的“包名称 + 标识符”的方式写清楚变量名或者函数名。
比如 random.randint(1,9) 再比如 math.cos(0)
这就类似于叫王强的人很多,但可以通过 志强小学.三年级二班.王强 的方式来命名,避免了重名的情况。
而在我们编写简单小程序的时候,不愿意写很长的函数名称,可以用简便写法:
from random import *
这样做的好处是用random库的函数非常方便,直接写randint就行,很省事。坏处是如果程序大了,几乎一定会发生重名的问题。
而且对于Python这种动态语言来说,重名会导致变量直接被覆盖,简直就是灾难:
def randint(): print("这是一个随机函数") from random import * randint() # 运行结果:TypeError: randint() missing 2 required positional arguments: 'a' and 'b' # 原因:import * 的时候直接把前面定义的randint覆盖掉了
所以结论是:
对于足够小的练习、测试程序,想怎么写怎么写。
一旦略微有一点规模的程序,就尽可能不要用from xxxx import * 的写法,一不小心就会发生变量重名的问题。只为省了几个字,惹出一大堆麻烦。
其实其它语言也是一样,比如C++的:using namespace xxx; 也会造成类似的问题。
————————回应评论补充——————————
方便性和安全性兼顾的写法,是这样:
from random import randint from time import sleep, clock
这样写既能方便的使用randint和sleep函数,不用加前缀;又能把潜在冲突放在一个极小的范围内。比用 星号* 好很多,很多大型项目都采用这种写法。