【问题标题】:Why do languages like Java use hierarchical package names, while Python does not? [closed]为什么像 Java 这样的语言使用分层包名,而 Python 没有? [关闭]
【发布时间】:2010-10-17 01:54:51
【问题描述】:

我没有用 Java 做过企业工作,但我经常看到 reverse-domain-name package naming convention。例如,对于 Stack Overflow Java 包,您可以将代码放在包 com.stackoverflow 下。

我刚刚遇到一个使用类似 Java 约定的 Python 包,我不确定支持和反对它的论据是什么,或者它们是否以与 Java 相同的方式适用于 Python。您更喜欢其中一个的原因是什么? 这些原因是否适用于所有语言?

【问题讨论】:

    标签: java python packages


    【解决方案1】:

    Python 不这样做是因为你最终会遇到一个问题——谁拥有几乎所有其他东西都是子包的“com”包? Python 建立包层次结构(通过文件系统层次结构)的方法根本不能很好地适应这种约定。 Java 可以摆脱它,因为包层次结构是由提供给“包”语句的字符串文字的结构定义的,因此任何地方都不需要显式的“com”包。

    还有一个问题是,如果您想公开发布一个软件包但没有一个适合嵌入软件包名称的域名,或者如果您最终更改(或丢失)您的域名以某些原因。 (以后的更新需要不同的包名吗?你怎么知道 com.nifty_consultants.nifty_utility 是 com.joe_blow_software.nifty_utility 的更新版本?或者,相反,你怎么知道它不是更新版本?如果你错过了你的域名更新,域名被域名露营者抢走了,别人从他们那里购买了域名,他们想公开发布软件包,他们是否应该使用你已经使用过的同一个名字? )

    在我看来,域名和软件包名称解决了两个完全不同的问题,并且具有完全不同的复杂因素。我个人不喜欢 Java 的约定,因为(恕我直言)它违反了关注点分离。避免命名空间冲突是一件好事,但我讨厌我的软件的命名空间是由(并依赖于)营销部门与某些第三方官僚机构的交互定义的。

    为了进一步澄清我的观点,回应 JeeBee 的评论:在 Python 中,包是一个包含 __init__.py 文件(并且可能是一个或多个模块文件)的目录。包层次结构要求每个更高级别的包都是完整的、合法的包。如果两个包(尤其是来自不同供应商,但甚至来自同一供应商的不直接相关的包)共享一个顶级包名称,无论该名称是 'com' 或 'web' 还是 'utils' 或其他任何名称,每一个必须为该顶级包提供__init__.py。我们还必须假设这些软件包可能安装在目录树中的同一位置,即 site-packages/[pkg]/[subpkg]。因此,文件系统强制要求只有 一个 [pkg]/__init__.py - 那么哪一个获胜?这个问题没有(也不可能)一个一般情况下的正确答案。我们也不能合理地将这两个文件合并在一起。由于我们无法知道另一个包在该__init__.py 中可能需要做什么,因此不能假定共享顶级包的子包在安装两者时都可以工作,除非它们被专门编写为相互兼容(至少在这个文件)。这将是一场分发噩梦,并且几乎会使嵌套包的整个点无效。这并不特定于反向域名包层次结构,尽管它们提供了最明显的坏例子并且(IMO)在哲学上是有问题的——这实际上是共享顶级包的实际问题,而不是哲学问题,即我主要关心这里。

    (另一方面,使用子包更好地组织自身的单个大包是一个好主意,因为这些子包专门设计用于一起工作和生活。这在 Python 中并不常见,不过,因为单个概念包往往不需要足够多的文件来需要额外的组织层。)

    【讨论】:

    • 你不必使用反向域名作为包名(这在英国很麻烦 - 我们使用“uk.co.company”,这很简陋吗?)这只是一种方式建立一个非正式的命名空间系统,到目前为止它工作得很好,我只是没有看到你的论点。
    • 实际上,在 Python 中,您必须对概念分发包层次结构进行自己的编码。也就是说,您最终会创建 akka_httpakka_actor(您想单独分发)而不是 akka.httpakka.actor 包,因为后者遇到了针对 akka 包的多个 __init__.py 的问题。如果您将包作为单个分发包分发,则不会遇到此问题。这对我来说似乎很不自然,也就是说,必须根据你计划如何分发它们来调整你的包的命名。
    【解决方案2】:

    如果 Guido 自己宣布应该遵循反向域约定,它不会被采用,除非在 python 中对 import 的实现有重大改变。

    考虑:python 在运行时使用快速失败算法搜索导入路径; java 在编译时和运行时都使用详尽的算法搜索路径。来吧,试着像这样安排你的目录:

    folder_on_path/
        com/
            __init__.py
            domain1/
                module.py
                __init__.py
    
    
    other_folder_on_path/
        com/
            __init__.py
            domain2/
                module.py
                __init__.py
    

    那就试试吧:

    from com.domain1 import module
    from com.domain2 import module
    

    其中一个语句会成功。为什么?因为folder_on_pathother_folder_on_path 在搜索路径上的位置更高。当 python 看到 from com. 时,它会抓取第一个 com 包。如果它恰好包含domain1,那么第一个import 将成功;如果没有,它会抛出一个ImportError 并放弃。为什么?因为import 必须在运行时出现,可能出现在代码流中的任何一点(尽管最常见的是在开始时)。没有人想要在这一点上进行详尽的树遍历来验证没有可能的匹配。它假设如果找到一个名为 com 的包,它就是 the com 包。

    此外,python 不区分以下语句:

    from com import domain1
    from com.domain1 import module
    from com.domain1.module import variable
    

    验证com com 的概念在每种情况下都会有所不同。在java中,你真的只需要处理第二种情况,这可以通过遍历文件系统来完成(我猜类和文件命名相同的好处)。在 python 中,如果您尝试仅使用文件系统帮助来完成导入,则第一种情况可以(几乎)透明地相同(init.py 不会运行),第二种情况可以完成,但是你会失去module.py的初始运行,但是第三种情况是完全无法实现的。代码必须执行才能使variable 可用。这是另一个要点:import 不仅仅解析命名空间,它还执行代码。

    现在,如果每个分发的 python 包都需要一个安装过程来搜索com 文件夹,然后是domain,等等,那么你可以侥幸逃脱,但这会使打包变得相当困难,破坏了拖放功能,并且使打包和全力以赴的麻烦。

    【讨论】:

    • from com.domain1 import module as d1m # etc.
    • 关于不同路径目录中的多个“com”包的好处——我只考虑共享单个父包的问题,​​但同名的单独包更糟糕。
    • Java 在运行时搜索类,而不仅仅是在编译时,使用目录结构。它是动态链接的。
    • 如果不可能在 __init__.py 文件中运行任意代码(这可以防止发生以下情况),我希望 Python 能够进行详尽的树遍历以搜索 com.domain2 模块.
    【解决方案3】:

    “您更喜欢其中一个的原因是什么?”

    Python 的风格更简单。 Java 的风格允许来自不同组织的同名产品。

    “这些原因是否适用于所有语言?”

    是的。您可以轻松地拥有名为“com”、“org”、“mil”、“net”、“edu”和“gov”的顶级 Python 包,并将您的包作为子包放入其中。

    编辑。执行此操作时您会遇到一些复杂性,因为每个人 都必须合作,而不是用自己的垃圾污染这些顶级包。

    Python 没有开始这样做是因为命名空间冲突——实际上——结果相当罕见。

    Java 开始这样做是因为开发 Java 的人预见到很多人会毫无头绪地为他们的包选择相同的名称,并且需要解决冲突和所有权问题。

    Java 人员没有预见到开源社区会选择奇怪的现成唯一名称来避免名称冲突。有趣的是,每个编写 xml 解析器的人都不称其为“解析器”。他们似乎称它为“撒克逊”或“Xalan”或完全奇怪的东西。

    【讨论】:

    • +1 没错,我从来没有在两个同名包之间发生过冲突。不过,您可能有两个名为django 的包,同时导入这两个包可能会很困难。看起来它在 Java 中可能是不可能,因为它没有导入名称重新映射。
    • 鉴于 Python 的文件系统到包的映射,使用“com”和“org”等顶级包实际上会创建命名空间冲突。
    • @Jeff S:这听起来像是一个很好的答案的开始!
    • @Jeff S 但是,这难道不是 Python 在您 导入 模块时让您更严格地控​​制命名空间的原因吗?将 com 导入为 com2,从 com 导入 sub 作为 sub1。 IMO,这比不得不依赖完全限定的类名要好得多。
    • @jholloway7:请参阅我编辑的关于 com/__init__.py 的答案,了解为什么导入别名在这里实际上没有帮助。
    【解决方案4】:

    在 Joel on Software 的某处,Joel 比较了公司发展的两种方法:Ben & Jerry 的方法,从小规模开始,然后有机地发展,以及亚马逊的方法,即筹集大量资金并提出非常广泛的要求从一开始。

    当 Sun 推出 Java 时,它大张旗鼓,大肆宣传。 Java应该接管。大多数未来相关的软件开发将在网络交付的 Java 小程序上进行。会有铜管乐队,甚至是小马。在这种情况下,明智的做法是预先建立一个基于互联网、对公司友好且在全球范围内的命名约定。

    好吧,结果并不像 Sun 所希望的那样,但他们计划着好像他们会成功一样。就个人而言,我鄙视可能会因成功而受到破坏的项目。

    Python 最初是 Guido van Rossum 的一个项目,而社区在很长一段时间后才确信如果 van Rossum 被公共汽车撞到,它能够生存下来。据我所知,最初并没有接管世界的计划,也没有打算用作网络小程序语言。

    因此,在语言的形成阶段,没有理由需要一个庞大的命名方案层次结构。在那个更非正式的社区中,一个人选择了一个或多或少异想天开的项目名称,并检查其他人是否已经在使用它。 (在英国喜剧节目之后命名计算机语言可能会被认为是异想天开的开始。)没有必要迎合一个庞大但缺乏想象力和笨拙的命名方案。

    【讨论】:

    • +10 表示历史背景和清晰的视角。但我只能 +1 :)
    • 我会尽我所能支持 +10 :)
    • “就我个人而言,我鄙视那些可能被成功破坏的项目。”我基本同意(虽然“鄙视”相当强烈),但与此同时,承诺拯救和正道的公司/项目给了我荨麻疹......
    【解决方案5】:

    这是防止名称冲突的好方法,并充分利用了现有的域名系统,因此不需要额外的官僚机构或注册。它简单而精彩。

    通过反转域名,它还赋予了它一个层次结构,这很方便。所以最后你可以有子包。

    唯一的缺点是名称的长度,但对我来说这根本不是缺点。我认为这对于任何支持它的语言来说都是一个不错的主意。

    例如,为什么 JavaScript 库不这样做?它们的全局命名空间是个大问题,但 Javascript 库使用简单的全局标识符,例如“$”,这与其他 Javascript 库发生冲突。

    【讨论】:

    • JavaScript 是一个完全独立的问题。 Python 具有文件级模块范围和导入机制,而 JavaScript 则没有。
    • 啊,我不知道 Python 可以做到这一点。不过,关于 Javascript 的评论仍然很重要。 Javascript 中的对象位于全局范围内,Java 中的包名称也是如此,因此可以从一些合理的命名约定中受益以防止冲突。
    • 对,这是有道理的——命名约定用于避免 Java 包导入中的冲突。但它在 Python 中是否必要/有用?
    • 抱歉,我对 Python 的了解不够,无法发表评论 - 我尝试阅读此 network-theory.co.uk/docs/pytut/PythonScopesandNameSpaces.html,这似乎表明它在 Python 中不是必需的,至少对于模块而言。
    【解决方案6】:

    这个想法是保持名称空间无冲突。而不是不可读的 UUID 或类似的,反向域名不太可能妨碍别人。 非常简单,但务实。 此外,在使用 3rd 方库时,它可能会为您提供有关它们来自何处的线索(用于更新、支持等)

    【讨论】:

    • 啊,我忘记了 Java 没有导入重映射机制(bugs.sun.com/bugdatabase/view_bug.do?bug_id=4983159),因此可能会发生冲突。这可能是原因。
    • 嗯,那太好了!导入 my.code.Enquiry;将 their.code.Enquiry 导入为 TheyEnquiry;类主体中的变量名/关键字甚至不一定会发生冲突,因为它在导入部分中。
    【解决方案7】:

    Python确实拥有它,它只是一个更扁平的层次结构。以os.path 为例。没有什么能阻止图书馆设计师做出更深层次的设计,例如姜戈。

    从根本上说,我认为 Python 的设计理念是您希望完成工作而无需提前指定或输入太多内容。这极大地有助于脚本和命令行的使用。 《Python 之禅》的几个部分说明了这一点:

    • 简单胜于复杂。
    • 平面优于嵌套。
    • 美胜于丑。 (Java 系统在我看来很难看。)

    另一方面,有:

    • 命名空间是一个非常棒的想法——让我们做更多这样的事情!

    【讨论】:

    • 我认为您是在回答问题的 标题 而不是内容——我真的很想知道反向域名层次结构。
    【解决方案8】:

    Java 能够这样做,因为它是推荐的 Java 标准实践,并且几乎被 Java 社区普遍接受。 Python 没有这个约定。

    【讨论】:

    • 我认为 python 很完美? :)
    • Python 完美的; Java 并不真正需要这个,他们只是选择这样做。 ;-)
    • Python 是完美的。只是 Java 使用了错误的约定 ;)
    • S.Lott,你比我快 2 秒。
    • @mP:问题是关于约定的。有明确的理由支持这两种方法。 Java 的一个好处是它提供了一种很好的清晰方式来导航包,并清楚地查看每个包的所属位置。
    猜你喜欢
    • 1970-01-01
    • 2010-10-17
    • 2011-12-31
    • 2013-04-19
    • 2011-11-16
    • 2011-03-30
    • 2011-01-29
    • 2017-07-31
    • 1970-01-01
    相关资源
    最近更新 更多