Ruby使用require
,Python使用import
。它们是完全不同的模型,虽然我更习惯于require
模型,但我可以看到一些我认为更喜欢import
的地方。我很好奇人们发现这些模型中的每一个都特别容易 - 或者更有趣,更难以应对 -
特别是,如果您正在编写新的编程语言,您将如何设计代码加载机制?哪种“专业”和“缺点”会对您的设计选择产生最大影响?
答案 0 :(得分:14)
Python import
有一个主要特点,它将两个东西联系在一起 - 如何找到导入,并在下包含它的名称空间。
这会创建非常明确的代码:
import xml.sax
这指定了根据Python搜索路径的规则在哪里找到我们想要使用的代码。
同时,我们要访问的所有对象都存在于此确切的命名空间下,例如xml.sax.ContentHandler
。
我认为这是Ruby的要求的优势。 require 'xml'
实际上可能会在命名空间XML
中创建对象,或者在模块中提供任何其他命名空间,而这一点在需求行中无法直接显示。
如果xml.sax.ContentHandler
太长,您可以在导入时指定其他名称:
import xml.sax as X
现在它在X.ContentHandler
下有效。
这种方式Python要求您显式构建每个模块的命名空间。因此,Python命名空间非常“物理”,我将解释我的意思:
例如,如果我们有一个包含内部子模块machine
和interface
的小型Python包“process”,我们希望将它作为一个方便的命名空间直接显示在包名下,这是和我们可以在“包定义”文件process/__init__.py
中编写的示例:
from process.interface import *
from process.machine import Machine, HelperMachine
因此,我们将process.machine.Machine
通常可以访问的内容提升到process.Machine
。我们以非常明确的方式将所有名称从process.interface
添加到process
命名空间。
我写的Python导入的优点只有两个:
import
答案 1 :(得分:3)
require
的一个不错的属性是它实际上是Kernel
中定义的方法。因此,您可以覆盖它并为Ruby实现自己的包装系统,例如, Rubygems呢!
PS:我不是在这里销售猴子补丁,但事实上Ruby的包系统可以被用户重写(甚至像python的系统那样工作)。当你编写一种新的编程语言时,你无法把一切都搞定。因此,如果您的导入机制完全可以从语言中扩展(完全向所有方向),那么您将为未来的用户提供最好的服务。从内部无法完全扩展的语言是一个进化的死胡同。我想这是Matz用Ruby做的事情之一。
答案 2 :(得分:1)
免责声明,我绝不是Python专家。
我看到require
超过import
的最大优势就是您不必担心理解命名空间和文件路径之间的映射。很明显:它只是一个标准的文件路径。
我非常喜欢强调import
具有的命名空间,但不禁想知道这种特殊方法是不是太不灵活了。据我所知,在Python中控制模块命名的唯一方法是更改正在导入的模块的文件名或使用as
重命名。此外,使用显式命名空间,您可以通过其方式通过其完全限定的标识符引用某些内容,但使用隐式命名空间,您无法在模块本身内执行此操作,这可能会导致潜在的歧义没有重命名就很难解决。
即,foo.py
:
class Bar:
def myself(self):
return foo.Bar
这失败了:
Traceback (most recent call last): File "", line 1, in ? File "foo.py", line 3, in myself return foo.Bar NameError: global name 'foo' is not defined
两种实现都使用一个位置列表进行搜索,无论您选择哪种模型,这都会让我感到非常重要。
如果使用像require
这样的代码加载机制,但语言根本没有全局命名空间怎么办?即,所有地方,每个地方都必须是命名空间,但开发人员可以完全控制定义类的命名空间,并且该命名空间声明在代码中显式发生,而不是通过文件名。或者,在全局命名空间中定义某些内容会生成警告。这是一个两全其美的方法,还是有一个明显的缺点,我错过了?
答案 3 :(得分:1)
Python的import提供了一种非常明确的命名空间:命名空间是路径,您不必查看文件以了解它们在其定义中使用的命名空间,并且您的文件不会被命名空间定义混乱。这使得应用程序的名称空间方案简单快速地理解(只需查看源代码树),并避免像命名空间声明一样的简单错误。
一个好的副作用是每个文件都有自己的私有命名空间,因此在命名时不必担心冲突。
有时名称空间也会变得很烦人,像some.module.far.far.away.TheClass()
这样的地方很快就会使你的代码变得很长而且很无聊。在这些情况下,您可以import ... from ...
并在当前名称空间中注入另一个名称空间。如果注入导致与您导入的模块发生冲突,则只需重命名导入的内容:from some.other.module import Bar as BarFromOtherModule
。
Python仍然容易受到循环导入等问题的影响,但它的应用程序设计不仅仅是在这些情况下必须归咎于的语言。
所以python使用了C ++ namespace
和#include
,并在很大程度上扩展了它。另一方面,我没有看到ruby的module
和require
以哪种方式添加任何新内容,并且你有完全相同的可怕问题,如全局命名空间混乱。