python中的这两个导入有什么区别?

dwbf0jvd  于 2023-01-29  发布在  Python
关注(0)|答案(4)|浏览(168)

我假设操作是相同的,但是为什么要导入同一个类的两个对象呢?使用第一种语法和使用第二种语法是否有特定的情况?在当前的文档(v2.1.x)中有第二种方法。

from itsdangerous import URLSafeTimedSerializer

from itsdangerous.url_safe import URLSafeTimedSerializer
hgtggwj0

hgtggwj01#

在一般情况下,这两者是不同的;一个从父包导入符号,另一个从子包导入符号。
在实践中,itsdangerous为了方便起见会通过父包提供相同的符号,因此在本例中,两者是等效的。
更一般地说,对于任何不存在这种便利机制的包,您可能希望其中一个抛出错误。
在病态的情况下,父对象和子对象可能拥有名称相同但内容完全不同的类或函数。

iqih9akk

iqih9akk2#

from itsdangerous

使用上面的导入意味着您是从Python项目中的整个'itsdaranger'库导入的。

from itsdangerous.url_safe

而使用上面的导入意味着您是从“itsdaranger”库中的“url_safe”模块导入的。
由于您只导入一个方法URLSafeTimedSerializer,因此使用哪个import语句不会有什么区别-因为它是“url_safe”模块的一部分。这将有助于解释器了解哪个模块包含该方法,而不是遍历整个库。
这也将帮助读者了解哪个模块包含该方法。

r7xajy2e

r7xajy2e3#

总结

在这个特定的例子中,itsdangerous库实现了一个别名,所以这两行import做同样的事情,别名from itsdangerous import URLSafeTimedSerializer是为了方便;该模块实际上在itsdangerous.url_safe包中定义。
许多现实世界的库使用这种技术,这样用户就可以选择是写更短的行还是显式地描述包结构。但是通过使用from ... import语法,类在代码中将被称为URLSafeTimedSerializer(没有任何前缀)。
其他一些现实世界的库使用这种技术来处理"内部"模块,这些模块的名称以_为前缀。其思想是用户不打算直接导入这些模块(或子包),但它们的内容仍然可以直接从包中获得。与编写一个大模块不同,制作这种包允许将实现拆分到多个文件中。
总的来说,from X import Z表示从X中取出Z并使用它。只有当X中实际包含Z时,此操作才有效。from X.Y import Z表示从X.Y中取出Z并使用它。只有当X.Y中包含Z时,此操作才有效。即使两个源代码都包含Z,它也不一定是相同的Z,但是,库作者可以安排X直接包含在X.Y中定义的相同Z

from ... import的工作原理

from X import Y有三种工作方式:

  1. X是一个包,Y是一个模块。如果需要,将加载包,然后加载模块。然后在代码中将模块分配给Y
  2. X是一个包,Y是一个类,如果需要的话会加载这个包,假设没有错误,Y已经是X的一个属性;它将被查找,并在代码中赋给Y
  3. X是模块,而Y是类。如果X在包中(这取决于用于X的语法,而不是文件夹结构上的),该软件包(以及任何父包)将被加载。假设没有错误,Y类将在X模块中找到,并在代码中被指定为名称Y
    上面的描述有点不精确,因为从Python的Angular 来看,包是一种模块--所以上面的所有内容都应该说是"非包模块",而不仅仅是"模块"。
    加载一个包不一定加载它包含的任何模块(包括子包),但是包的__init__.py(如果存在)可以显式地import这些东西来加载它们。加载一个包的一部分模块,***一定要将它作为包的属性附加。(它也一定要加载包;否则,将没有任何东西可附加。)
    加载的所有内容都按名称缓存;尝试以相同的名称再次加载它将返回缓存的module对象。

类如何成为包和其他模块的一部分?

注意,只有包和模块被"加载"(即导入),而不是类。module对象是在模块文件的所有顶层代码运行之后,代表模块文件源代码中所有全局变量的东西。
对于普通的模块来说,这很简单,对于包来说,"顶层代码"可能包含在一个名为__init__.py的特殊文件中。

顶级包如何为它的某个模块中定义的类取别名?

简单:它只是使用相同的from ... import语法显式地将import作为模块。记住,导入是缓存的,所以这不会导致冲突或浪费时间;并且它将类名作为包代码中的全局变量进行赋值-这意味着,当包被加载时,它将成为包的一个属性。
同样,加载包不会自动加载其包含的模块;但是显式地加载它们(使用__init__.py)允许包在加载它们之后别名其模块的内容。
我们可以看到in the source code

from .url_safe import URLSafeTimedSerializer as URLSafeTimedSerializer

(The这里使用as是多余的,因为类实际上没有重命名。但是,有时这些别名会重命名一些东西,以避免命名冲突。)
以下内容:当itsdangerous封装(作为一个包,是一个module对象),它将显式加载其包含的url_safe模块。它从url_safe获取URLSafeTimedSerializer属性(也是一个module),将其重命名为URLSafeTimedSerializer,这是itsdangerous/__init__.py代码中的一个全局变量。因为它是全局变量,当itsdangerous对象被创建(并存储在模块缓存中)时,它将拥有一个URLSafeTimedSerializer属性,即类,这反过来又允许用户的代码写入from itsdangerous import URLSafeTimedSerializer,即使URLSafeTimedSerializer没有在那里定义。

nukf8bse

nukf8bse4#

在这两个示例中,导入的是在itsdangerous.url_safe中定义的同一个类URLSafeTimedSerializer
第一个:from itsdangerous import URLSafeTimedSerializer的工作原理与第二个相同:from itsdangerous.url_safe import URLSafeTimedSerializer,因为在itsdangerous模块中没有其他具有冲突名称的工件。
我还想指出的是,认为第二次导入不会加载完整的itsdangerous在技术上是不正确的。在这两种情况下,整个itsdangerous都会加载到sys.modules中,并且在这两种情况下,URLSafeTimedSerializer都会绑定到sys.modules['itsdangerous'].url_safe。请查看answer以了解更多信息。性能-由于itsdangerous模块在两种情况下都被加载,因此它们也是类似的。
第二次导入相对于第一次导入的一个优点是它有助于可读性,如果有人想查看URLSafeTimedSerializer的定义(不需要访问一些自动查找引用的ide工具),他们可以很容易地知道他们必须查看url_safe
另一个优点是增加了代码的弹性,如果由于某种原因,itsdangerous的新版本在url_safe之外有URLSafeTimedSerializer的其他定义(老实说,这是糟糕的编码实践,但是嘿,这是完全可能的:)),并且您的包管理器安装了这个新版本的模块,那么from itsdangerous import URLSafeTimedSerializer将开始遇到问题。

相关问题