我假设操作是相同的,但是为什么要导入同一个类的两个对象呢?使用第一种语法和使用第二种语法是否有特定的情况?在当前的文档(v2.1.x)中有第二种方法。
from itsdangerous import URLSafeTimedSerializer from itsdangerous.url_safe import URLSafeTimedSerializer
hgtggwj01#
在一般情况下,这两者是不同的;一个从父包导入符号,另一个从子包导入符号。在实践中,itsdangerous为了方便起见会通过父包提供相同的符号,因此在本例中,两者是等效的。更一般地说,对于任何不存在这种便利机制的包,您可能希望其中一个抛出错误。在病态的情况下,父对象和子对象可能拥有名称相同但内容完全不同的类或函数。
itsdangerous
iqih9akk2#
from itsdangerous
使用上面的导入意味着您是从Python项目中的整个'itsdaranger'库导入的。
from itsdangerous.url_safe
而使用上面的导入意味着您是从“itsdaranger”库中的“url_safe”模块导入的。由于您只导入一个方法URLSafeTimedSerializer,因此使用哪个import语句不会有什么区别-因为它是“url_safe”模块的一部分。这将有助于解释器了解哪个模块包含该方法,而不是遍历整个库。这也将帮助读者了解哪个模块包含该方法。
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。
import
from itsdangerous import URLSafeTimedSerializer
itsdangerous.url_safe
from ... import
URLSafeTimedSerializer
_
from X import Z
X
Z
from X.Y import Z
X.Y
from X import Y有三种工作方式:
from X import Y
Y
__init__.py
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没有在那里定义。
as
url_safe
itsdangerous/__init__.py
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将开始遇到问题。
from itsdangerous.url_safe import URLSafeTimedSerializer
sys.modules
sys.modules['itsdangerous'].url_safe
4条答案
按热度按时间hgtggwj01#
在一般情况下,这两者是不同的;一个从父包导入符号,另一个从子包导入符号。
在实践中,
itsdangerous
为了方便起见会通过父包提供相同的符号,因此在本例中,两者是等效的。更一般地说,对于任何不存在这种便利机制的包,您可能希望其中一个抛出错误。
在病态的情况下,父对象和子对象可能拥有名称相同但内容完全不同的类或函数。
iqih9akk2#
使用上面的导入意味着您是从Python项目中的整个'itsdaranger'库导入的。
而使用上面的导入意味着您是从“itsdaranger”库中的“url_safe”模块导入的。
由于您只导入一个方法URLSafeTimedSerializer,因此使用哪个import语句不会有什么区别-因为它是“url_safe”模块的一部分。这将有助于解释器了解哪个模块包含该方法,而不是遍历整个库。
这也将帮助读者了解哪个模块包含该方法。
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
有三种工作方式:X
是一个包,Y
是一个模块。如果需要,将加载包,然后加载模块。然后在代码中将模块分配给Y
。X
是一个包,Y
是一个类,如果需要的话会加载这个包,假设没有错误,Y
已经是X
的一个属性;它将被查找,并在代码中赋给Y
。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:
(The这里使用
as
是多余的,因为类实际上没有重命名。但是,有时这些别名会重命名一些东西,以避免命名冲突。)以下内容:当
itsdangerous
封装(作为一个包,是一个module
对象),它将显式加载其包含的url_safe
模块。它从url_safe
获取URLSafeTimedSerializer
属性(也是一个module
),将其重命名为URLSafeTimedSerializer
,这是itsdangerous/__init__.py
代码中的一个全局变量。因为它是全局变量,当itsdangerous
对象被创建(并存储在模块缓存中)时,它将拥有一个URLSafeTimedSerializer
属性,即类,这反过来又允许用户的代码写入from itsdangerous import URLSafeTimedSerializer
,即使URLSafeTimedSerializer
没有在那里定义。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
将开始遇到问题。