问题

如何理解“from xxx import *这种写法会给你带来无穷无尽的噩梦?”?

回答
“`from xxx import ` 这种写法会给你带来无穷无尽的噩梦”——这句看似夸张的警告,在 Python 开发中却有着深刻的道理。它直指这种导入方式的几个核心弊端,这些弊端一旦累积起来,确实可能让你的代码维护和调试变成一场噩梦。

让我们深入剖析一下,为什么 `from xxx import ` 会带来如此负面的影响:

1. 命名空间污染与冲突 (Namespace Pollution and Conflicts)

这是最核心也最普遍的问题。

什么是命名空间? 在 Python 中,每个变量、函数、类都属于一个特定的命名空间。你可以把它想象成一个容器,里面装着名字和它们对应的对象。模块(如 `.py` 文件)本身就是一个独立的命名空间。
`import xxx` 的行为: 当你使用 `import xxx` 时,你将 `xxx` 模块的整个命名空间引入了当前作用域,但你需要通过 `xxx.函数名` 或 `xxx.变量名` 来访问其中的内容。这种方式清晰地表明了某个对象来自哪个模块。
`from xxx import ` 的行为: 当你使用 `from xxx import ` 时,Python 会将 `xxx` 模块中 所有 公共(非以 `_` 开头)的名称直接导入到你当前的命名空间中。这意味着,你可以在当前作用域内直接使用 `函数名` 或 `变量名`,而不需要模块前缀。

噩梦场景:

想象一下,你正在开发一个大型项目,项目中有很多模块。你可能在不同的模块中定义了同名的函数或变量,例如:

模块 A (`module_a.py`):
```python
def process_data(data):
print("Processing data in Module A")
return data 2
```
模块 B (`module_b.py`):
```python
def process_data(data):
print("Processing data in Module B")
return data + 10
```

现在,你在另一个文件 `main.py` 中写道:

```python
Scenario 1: Explicit Import (Good)
import module_a
import module_b

print(module_a.process_data(5)) Output: Processing data in Module A 10
print(module_b.process_data(5)) Output: Processing data in Module B 15

Scenario 2: Using from ... import (Problematic)
from module_a import
from module_b import This will overwrite module_a's process_data

print(process_data(5)) Which process_data will be called?
```

在 Scenario 2 中,当 `from module_b import ` 执行时,`module_b` 中的 `process_data` 函数会 覆盖 已经从 `module_a` 导入的同名函数。最终,当你调用 `process_data(5)` 时,执行的是 `module_b` 的函数,而不是 `module_a` 的。

噩梦的根源:

不可预知性: 你不知道有多少名称被导入到当前命名空间。
难以追踪来源: 当你看到一个函数名时,你无法直接知道它来自哪个模块,尤其是在项目规模变大、模块增多的情况下。
隐藏的覆盖: 后导入的同名对象会静默地覆盖先导入的对象,导致难以发现的错误。

2. 代码可读性差与维护困难 (Poor Readability and Maintenance)

正如上面所说,`from xxx import ` 严重破坏了代码的可读性。

难以理解代码意图: 阅读代码的人需要花费更多精力去追溯每一个函数的来源。当一个文件中充斥着大量 `from xxx import ` 时,代码看起来就像是从各种地方“粘贴”过来的,缺乏清晰的结构和逻辑。
重构的噩梦: 如果你需要修改一个模块中的函数名,或者删除一个不再使用的函数,使用 `from xxx import ` 的代码会让你难以判断哪些地方会受到影响。你可能需要grep(搜索)整个项目,而且即使这样,也可能因为命名冲突而遗漏问题。
协作的挑战: 在团队协作中,这种导入方式会使代码变得更加难以理解和维护,增加了沟通成本和出错的可能性。

3. 导入未使用的代码,增加不必要的依赖 (Importing Unused Code and Unnecessary Dependencies)

`from xxx import ` 会导入模块中的 所有 公共名称。即使你只需要其中一个或两个函数,其余的名称也会被导入。

噩梦场景:

假设一个模块 `utils.py` 包含了很多工具函数:

```python
utils.py
def func_a(): pass
def func_b(): pass
def func_c(): pass
def func_d(): pass
... 还有很多其他函数
```

而你在 `main.py` 中只需要 `func_a`:

```python
from utils import Imports ALL functions, even though only func_a is used

func_a()
```

这样做的后果是:

增加内存占用和加载时间: Python 需要加载并解析 `utils.py` 中的所有内容,即使你只使用其中一小部分。对于大型模块,这会成为一个性能瓶颈。
可能引入不必要的 side effects: 有些函数在导入时可能执行某些操作(尽管这是不推荐的实践),或者它们可能依赖于其他尚未被理解的全局变量或状态。`from xxx import ` 会无差别地执行这些操作。
代码审查的负担: 代码审查者需要检查导入的名称是否都得到了使用,这增加了审查的工作量。

4. 误导和混淆 (Misdirection and Confusion)

`from xxx import ` 的写法会让你认为某个函数或变量是“本地”定义的,因为它没有模块前缀。这会误导你对代码的理解,尤其是在代码量大、逻辑复杂的时候。

噩梦场景:

你看到一个函数调用 `calculate_total(price, tax)`。在没有 `from xxx import ` 的情况下,如果 `calculate_total` 是一个公共函数,你会写 `from my_calculator import calculate_total` 或者 `import my_calculator` 然后调用 `my_calculator.calculate_total()`。这会让你知道 `calculate_total` 的来源。

但如果使用 `from my_calculator import `,那么 `calculate_total` 就和你在当前文件中直接定义的函数一样,你可能会错误地以为它是一个简单的辅助函数,而忽略了它可能包含复杂的逻辑、依赖于特定模块的全局状态,或者在其他地方有同名函数被覆盖。

总结一下“无穷无尽的噩梦”是如何形成的:

1. 起始的混乱: 你引入了一个不透明的黑盒子,不知道里面有什么。
2. 隐藏的冲突: 随着项目发展,同名函数/变量被引入并默默覆盖,导致难以追踪的逻辑错误。
3. 维护的痛苦: 改动一点点代码都可能牵一发而动全身,调试过程如同大海捞针。
4. 性能的负担: 不必要的导入拖慢了程序的加载速度和执行效率。
5. 协作的障碍: 其他开发者难以理解你的代码,提高了项目整体的维护成本。

正确的导入方式推荐:

为了避免这些噩梦,Python 社区普遍推崇以下更清晰、更安全的导入方式:

显式导入特定名称:
```python
from module_a import func_a, ClassB
```
这明确地告诉你 `func_a` 和 `ClassB` 来自 `module_a`。

使用模块别名:
```python
import module_a as ma
```
然后使用 `ma.func_a()` 来访问。这在模块名过长或为了避免命名冲突时非常有用。

导入整个模块:
```python
import module_a
```
然后使用 `module_a.func_a()` 来访问。这是最安全和最清晰的方式,尤其是在大型项目或可能存在命名冲突的情况下。

例外情况 (谨慎使用):

在极少数情况下,例如在交互式环境中进行探索性编程,或者导入一些非常小的、功能单一且不会与其他命名冲突的模块时,`from xxx import ` 可能会带来便利。但即使在这种情况下,也建议在完成探索后,将 `` 改为显式导入。

总而言之,“`from xxx import ` 这种写法会给你带来无穷无尽的噩梦”这句话是对一种不良编程习惯的强烈警告,它揭示了这种导入方式在可读性、可维护性、可预测性和性能方面的严重缺陷,这些缺陷在项目规模扩大、复杂度增加时会被无限放大。

网友意见

user avatar

你写了一个程序:

       from some_module import * print("Hello World!")     

怎么运行都没有打印结果,然后打开some_module.py一看

       def print(*args):     exit(1)      

你气不气?

这就算善良的,它要给你写个os.remove() 之类的,你不更气?

当然了这是个很极端的情况,真的有恶意代码也不是不用from X import *就能解决的。这种写法的核心问题就是命名空间的冲突,因为你根本不知道你import进来了啥。

写程序这个事情,几乎在任何情况下,白名单都优于黑名单。白名单是可控的,黑名单是总会有漏网之鱼的。当你去拿车库拿工具的时候,需要锤子就拿锤子,需要锯子就拿锯子,不要把那个巨大的工具箱直接拿来,因为里面可能藏着爸爸拿来挖矿的炸药,见点火星子你就上天了。

在使用库的时候,基本上没有人对这个库是100%熟悉的,那你就只引入你想用的东西,显式地引入,这样可以在完全不了解这个库的情况下发现命名冲突的问题。如果你做了import *,那很可能会有大量的你并不熟悉甚至完全不知道的内容被引入,然后发生冲突,产生非常诡异的bug,严重浪费你的时间。

我不同意只要语言允许的,就应该去使用;就像我不同意家里存在的东西,小孩子就能玩一样。在使用任何一门语言的时候,都应该尽量选择这个语言允许范围内的效率更高、错误率更低的写法,这才是一个合格的程序员。

user avatar

谢邀。

from xxx import * 的后果

大家都讲得很明白了,from xxx import * 这种方法可能造成命名冲突。

例如A是卖数码产品的,B是卖水果的。A和B服务于同一个老板。他们都各有一个行李箱,现在他们都在老板面前,给老板供货。现在有两种情况:

  1. 在箱子里看一件件,要什么才拿出来(from B import apple)
  2. 为了方便,全部倒出来(from A import *, from B import * )

现在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 *,但这是我的特权,你们不能学我」吧?

所以,倒不如直接一刀切,制定好代码规范。让人自律很难,但让人遵守规则,则容易得多

user avatar

可以抽象一点看代码

       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 的地方。

user avatar

这个问题本质很简单:因为给孩子起名怕重名,写程序同样也怕重名。

几乎所有的现代编程语言都采用这种方法避免重名:用完整的“包名称 + 标识符”的方式写清楚变量名或者函数名。

比如 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函数,不用加前缀;又能把潜在冲突放在一个极小的范围内。比用 星号* 好很多,很多大型项目都采用这种写法。

类似的话题

  • 回答
    “`from xxx import ` 这种写法会给你带来无穷无尽的噩梦”——这句看似夸张的警告,在 Python 开发中却有着深刻的道理。它直指这种导入方式的几个核心弊端,这些弊端一旦累积起来,确实可能让你的代码维护和调试变成一场噩梦。让我们深入剖析一下,为什么 `from xxx import .............
  • 回答
    这句话“文官的衣服上绣的是禽,武官的衣服上绣的是兽。披上了这身皮,我们哪一个不是衣冠禽兽”融合了历史、文化、隐喻和讽刺,需要从多个层面进行解析: 一、历史背景与服饰象征1. 古代官服制度 在中国历史上,官服的纹饰(如禽鸟、兽类)是等级制度和身份象征的重要标志。 文官:常以“禽”为纹.............
  • 回答
    “自称迪士尼在逃公主”的现象在网络上出现后,引发了广泛讨论。这一说法通常指一些女性在社交媒体、论坛或网络社区中自称是“迪士尼公主”,并可能涉及身份扮演、文化认同、心理需求等多重层面。以下从多个角度详细分析这一现象的可能内涵和背景: 一、文化符号的再诠释:迪士尼公主的象征意义1. 迪士尼公主的原始形象.............
  • 回答
    自由主义和新自由主义是两种重要的思想体系,它们在政治哲学、经济学和社会政策等领域具有深远的影响。以下是对这两个概念的详细解析: 一、自由主义的定义与核心特征自由主义(Liberalism)是一种以个人自由、法治、民主和理性为价值基础的政治哲学思想体系,其核心在于保障个体权利和限制国家权力。自由主义的.............
  • 回答
    无政府主义(Anarchism)是一种深刻批判国家权力、追求个体自由与社会平等的政治哲学和实践运动。它并非主张“混乱”或“无序”,而是反对一切形式的强制性权威,尤其是国家对个人生活的控制。以下从多个维度深入解析这一复杂的思想体系: 一、核心定义与本质特征1. 对国家的彻底否定 无政府主义者认.............
  • 回答
    “爱国家不等于爱朝廷”这句话在理解中国古代政治和文化时非常重要。它揭示了国家与政权(即朝廷)之间的区别,以及臣民对这两者的情感和责任的不同层面。要理解这句话,我们需要先拆解其中的概念: 国家(Guó Jiā): 在古代,我们通常将其理解为国家的疆土、人民、文化、民族认同和长期的历史延续。它是根植.............
  • 回答
    理解中国人民银行工作论文中提到的“东南亚国家掉入中等收入陷阱的原因之一是‘文科生太多’”这一论断,需要从多个层面进行深入分析,因为这是一个相对复杂且具有争议性的议题。下面我将尽量详细地解释其背后的逻辑和可能含义:一、 背景:中等收入陷阱首先,我们需要理解什么是“中等收入陷阱”。 定义: 中等收入.............
  • 回答
    郭主席对房地产的表述“不希望房地产剧烈波动”可以从多个层面来理解,这背后反映了他对中国经济稳定和健康发展的深切关切。要详细理解这一点,我们需要从房地产在中国经济中的地位、波动可能带来的影响、以及“不剧烈波动”的具体含义等角度进行分析。一、 房地产在中国经济中的特殊地位:首先,理解为什么房地产会引起如.............
  • 回答
    如何理解科幻小说《时间的二分法》? 详细解读科幻小说《时间的二分法》(英文原名:The Time Machine),由英国著名作家赫伯特·乔治·威尔斯(H.G. Wells)于1895年创作,是科幻文学史上的经典之作。这部小说不仅为我们描绘了一个令人着迷的未来世界,更通过其深刻的社会寓言和哲学思考,.............
  • 回答
    尹建莉老师关于“延迟满足是鬼话,孩子要及时满足”的观点,确实在教育界引发了不少讨论。要理解她的观点,我们需要深入探讨她为什么会提出这样的论断,以及她所强调的“及时满足”的真正含义。首先,我们来拆解一下“延迟满足”这个概念及其传统理解。传统理解的“延迟满足”:延迟满足(Delayed Gratific.............
  • 回答
    理解外交部发言人陆慷的说法,即“《中英联合声明》作为一个历史文件,不再具有任何现实意义”,需要从几个关键角度来解读:1. 历史文件的定义与性质: 历史文件是过去的产物: 陆慷的表述首先强调了《中英联合声明》的“历史文件”属性。这意味着它是在特定历史时期、基于当时国际政治格局和两国关系背景下签署的.............
  • 回答
    杨振宁先生作为一位享誉世界的物理学家,他关于中美教育的评论引起了广泛关注和讨论。理解他的话需要从多个角度进行深入剖析,包括他所处的时代背景、他对教育本质的理解、以及他观察到的中美教育体系的差异。一、 杨振宁先生评论的时代背景与个人经历:首先,要理解杨振宁先生的话,必须考虑到他所处的时代背景和他的个人.............
  • 回答
    “中国是发达国家的粉碎机”这个说法,虽然带有一定的情绪化和夸张色彩,但其核心要表达的是:中国凭借其独特的经济模式、庞大的市场规模、强大的制造能力和不断进步的科技创新,对传统发达国家在经济和产业领域构成了前所未有的挑战,并在一定程度上“粉碎”了它们原有的竞争优势和发展路径。为了详细理解这一说法,我们可.............
  • 回答
    “爱国主义是流氓的最后一块遮羞布”这句话,最早出自塞缪尔·约翰逊(Samuel Johnson),一位杰出的18世纪英国作家和评论家。这句话的含义深刻且复杂,通常被用来讽刺和批评那些打着爱国旗号,但实际上在追求个人利益、制造分裂或煽动仇恨的人。要理解这句话,我们可以从以下几个层面来深入剖析:1. 字.............
  • 回答
    “Control is Dead”这句话的含义非常丰富且具有多层次的解读,它不是一个简单的字面陈述,而是对当前社会、技术、政治、经济等领域中一种普遍的失控感、权力分散化、个体自主性增强以及传统权威式微的深刻反映。要理解这句话,我们需要从不同的角度去剖析:一、 字面含义与引申含义: 字面含义: 最.............
  • 回答
    “小孩子才分对错,成年人只看利弊”这句话,乍一听可能有些功利甚至冷酷,但深入剖析,它揭示了一种关于成长、认知和处世态度的深刻变化。这句话并不是说成年人完全泯灭了道德感,而是强调在复杂的社会现实中,判断的侧重点会发生微妙而重要的转移。我们来详细地理解这句话的各个层面:一、 “小孩子才分对错”:儿童的认.............
  • 回答
    这句话以一种诗意且深刻的方式,阐述了科学与宗教(在此特指佛学)在追求真理和理解宇宙本质上可能存在的殊途同归。要理解它,我们可以从几个层面进行剖析:一、 表象的理解:科学探索的艰难与佛学智慧的超前 科学探索的“爬山”隐喻: 科学研究是一个漫长、艰辛、充满挑战的过程。科学家们如同登山者,需要克服无数.............
  • 回答
    “Don't judge”(别评判)这句简单的话语,却蕴含着深刻的道理,它不仅仅是一个简单的行为准则,更是一种生活态度和哲学。要理解它,需要从多个层面去深入剖析。核心含义:停止对他人进行预设的、带有偏见的、负面判断。“评判”(judge)这个词在中文里可以有几种理解: 审判(legal cont.............
  • 回答
    这句话, "对他们的伟大人物忘恩负义,这是伟大民族的标志",是一句富有争议且深刻的论断。要理解它,我们需要从多个层面进行剖析,包括字面含义、潜在的哲学思想、历史现实以及它可能带来的积极或消极影响。核心解读:反思与进步的动力从最核心的角度来看,这句话并非在鼓吹忘恩负义的行为本身是值得赞扬的,而是指向了.............
  • 回答
    「看山是山,看山不是山,看山还是山」,这句禅语,通常被称为“禅宗三境界”或者“悟道三阶段”,意境深远,历久弥新。它并非指代实际的山,而是用“山”这个意象来比喻一个人对事物、对真理、对自我的认知过程。理解这三层境界,能帮助我们更深刻地认识自己,认识世界。下面我将详细阐述这三层境界的含义:第一层境界:看.............

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有