python - 从 werkzeug 导入安全导入 werkzeug VS

标签 python python-3.x python-import python-3.6 werkzeug

我目前对 Python 中 import 如何工作的理解(基于这些答案: onetwothree ;和 Python documentation )是(以防万一:所有代码片段在 Python 3.6.1 上测试):

假设我们有一个模块 mod,它有子模块 subsub1sub 又具有一个函数 func;那么我们就可以(当然,假设 mod 安装在当前环境中):

import mod

mod.sub.func()
mod.sub1

# or
import mod.sub

mod.sub.func()
mod.sub1 # will result in "NameError: name 'mod' is not defined"

# or
from mod.sub import func

func()
mod.sub.func() # will result in "NameError: name 'mod' is not defined"
mod.sub1 # will result in "NameError: name 'mod' is not defined"

最近,在 Python 控制台中使用 werkzeug.security.generate_password_hashwerkzeug.security.check_password_hash 时,我注意到:

import werkzeug

werkzeug.security.generate_password_hash('some_password', method='pbkdf2:sha512', salt_length=25)

导致AttributeError:模块“werkzeug”没有属性“security”

不过,以下方法可以正常工作:

from werkzeug import security

security.generate_password_hash('some_password', method='pbkdf2:sha512', salt_length=25)

这(当然)也是:

import werkzeug.security

werkzeug.security.generate_password_hash('some_password', method='pbkdf2:sha512', salt_length=25)

还有这个:

from werkzeug.security import generate_password_hash

generate_password_hash('some_password', method='pbkdf2:sha512', salt_length=25)

而且,有点令人惊讶(至少对我来说),这个:

import werkzeug
from werkzeug import security

werkzeug.security.generate_password_hash('some_password', method='pbkdf2:sha512', salt_length=25)

我的问题是:

  1. 关于 import 在 Python 中如何工作,我的一些想法是否错误(或缺乏细节)?
  2. 为什么 import werkzeug 不允许我访问 werkzeug.security?我的理解是 - 它应该导入 werkzeug 及其所有子模块/属性。
  3. 为什么 import werkzeug + from werkzeug import security 允许访问 werkzeug.security?我的理解:它应该绑定(bind)两个单独的名称(它们之间没有联系),如下所示:werkzeugimport werkzeug(即 werkzeug 模块)和 securityfrom werkzeug import security (即 werkzeug 模块的 security 子模块。

最佳答案

我不确定我是否能够很好地回答您的所有问题,但我发现它很有趣并进行了查看,这是我的结果。

一般来说,import mod.subfrom mod import sub 假定 submod 中的子模块 包。但是,这也可能意味着 sub 是在 mod 模块中声明的字段/变量。

存在 __init.py__ 文件 will denote,表明文件夹是一个包:

The __init__.py files are required to make Python treat the directories as containing packages; this is done to prevent directories with a common name, such as string, from unintentionally hiding valid modules that occur later on the module search path. In the simplest case, __init__.py can just be an empty file, but it can also execute initialization code for the package (...).

我相信,from werkzeug import securityimport werkzeug.security都导入了一个模块security,因此security.generate_password_hash 是已知且有效的属性。基本上,from werkzeug.security importgenerate_password_hash通过有效的导入语句直接导入该属性。

Werkzeug Quickstart 文档中,我发现了以下内容:

Make sure to import all objects from the places the documentation suggests. It is theoretically possible in some situations to import objects from different locations but this is not supported.

此外,Werkzeug transition to 1.0 指出:

Werkzeug originally had a magical import system hook that enabled everything to be imported from one module and still loading the actual implementations lazily as necessary. Unfortunately this turned out to be slow and also unreliable on alternative Python implementations and Google’s App Engine.

Starting with 0.7 we recommend against the short imports and strongly encourage starting importing from the actual implementation module. Werkzeug 1.0 will disable the magical import hook completely.

看起来 Werkzeug modifies 模块是如何加载的。 (我推测,这在具有贡献内容的大型包中并不罕见,例如 Flask、Django;其动机是延迟加载、提高性能或管理跨包传播的贡献模块内容的能力。)

正如您所发现的,import werkzeug 不会werkzeug 模块导入 security,因为 (据我了解),唯一将作为属性导入的子模块是在 __init__.py 的 line 100 上定义的子模块:

# modules that should be imported when accessed as attributes of werkzeug
attribute_modules = frozenset(['exceptions', 'routing'])

在同一文件中,当 looking at 时,Werkzeug 的 module(ModuleType) 类及其 __getattr__() 方法:

class module(ModuleType):

    """Automatically import objects from the modules."""

    def __getattr__(self, name):
        if name in object_origins:
            module = __import__(object_origins[name], None, None, [name])
            for extra_name in all_by_module[module.__name__]:
                setattr(self, extra_name, getattr(module, extra_name))
            return getattr(module, name)
        elif name in attribute_modules:
            __import__('werkzeug.' + name)
        return ModuleType.__getattribute__(self, name)

看来object_origins字典中的模块名称,通过all_by_module中的定义,必须单独导入,并且werkzeug.securityone of them .

最后,我认为原因是:

import werkzeug     
from werkzeug import security  

组合有效,第一行导入安全性,但第二行导入,以及__getattr__()方法将返回显式导入的模块。

编辑: 最后一部分不正确,由 Filipp 测试:

我希望仅通过执行 from werkzeug import security 仍然可以 werkzeug.security.generate_password_hash() 工作。 (我还没有测试或证实这一点)

关于python - 从 werkzeug 导入安全导入 werkzeug VS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47688957/

相关文章:

class - Python 3 类中的全局变量

python - 如何解决这个 Python 导入循环引用

python - PIL 对象中的属性错误

Python 文件 read() 和 readline() 计数器?

python - 如何在 Python 中获取视频的时长?

python - 在python中并行化for循环

python - 修改后重新加载模块

python - 在 Python 中重命名命名参数以避免与 import 语句发生命名冲突

python - 如何从文本图像 OpenCV 中删除非直对角线?

python - 在 python 3 中使用 cElementTree