【问题标题】:How does SQLAlchemy create_engine import Engine class?SQLAlchemy create_engine 如何导入 Engine 类?
【发布时间】:2018-01-15 18:56:53
【问题描述】:

我对 python 比较陌生,并且正在尝试使用 SQLAlchemy。我注意到,要创建引擎,我必须使用通过from sqlalchemy import create_engine 导入的create_engine() 函数。

现在,create_engine 函数返回 sqlalchemy.engine.base.Engine 类的实例。但是我从来没有导入过这个类,我只导入了create_engine 模块。那么,Python 是如何知道sqlalchemy.engine.base.Engine 类的呢?

【问题讨论】:

标签: python sqlalchemy


【解决方案1】:

你可能不明白导入的作用。

Python全局导入模块。有一个名为sys.modules 的结构,它将导入的模块存储为字典:

>>> import sys
>>> sys.modules
{'builtins': <module 'builtins' (built-in)>, 'sys': <module 'sys' (built-in)>, '_frozen_importlib': <module 'importlib._bootstrap' (frozen)>, '_imp': <module '_imp' (built-in)>, ...}

当您导入 SQLAlchemy 时,您会导入一个 ,这是一种由多个模块组成的结构,其中一个导入会触发多个导入。所有这些导入的模块都存储在同一个地方:

>>> import sqlalchemy
>>> [name for name in sys.modules if 'sqlalchemy' in name]
['sqlalchemy', 'sqlalchemy.sql', 'sqlalchemy.sql.expression', 'sqlalchemy.sql.visitors', 'sqlalchemy.util', 'sqlalchemy.util.compat', 'sqlalchemy.util._collections', 'sqlalchemy.util.langhelpers', 'sqlalchemy.exc', 'sqlalchemy.util.deprecations', 'sqlalchemy.sql.functions', 'sqlalchemy.sql.sqltypes', 'sqlalchemy.sql.elements', 'sqlalchemy.inspection', 'sqlalchemy.sql.type_api', 'sqlalchemy.sql.operators', 'sqlalchemy.sql.base', 'sqlalchemy.sql.annotation', 'sqlalchemy.processors', 'sqlalchemy.cprocessors', 'sqlalchemy.event', 'sqlalchemy.event.api', 'sqlalchemy.event.base', 'sqlalchemy.event.attr', 'sqlalchemy.event.registry', 'sqlalchemy.event.legacy', 'sqlalchemy.sql.schema', 'sqlalchemy.sql.selectable', 'sqlalchemy.sql.ddl', 'sqlalchemy.util.topological', 'sqlalchemy.sql.util', 'sqlalchemy.sql.dml', 'sqlalchemy.sql.default_comparator', 'sqlalchemy.sql.naming', 'sqlalchemy.events', 'sqlalchemy.pool', 'sqlalchemy.log', 'sqlalchemy.interfaces', 'sqlalchemy.util.queue', 'sqlalchemy.engine', 'sqlalchemy.engine.interfaces', 'sqlalchemy.sql.compiler', 'sqlalchemy.sql.crud', 'sqlalchemy.engine.base', 'sqlalchemy.engine.util', 'sqlalchemy.cutils', 'sqlalchemy.engine.result', 'sqlalchemy.cresultproxy', 'sqlalchemy.engine.strategies', 'sqlalchemy.engine.threadlocal', 'sqlalchemy.engine.url', 'sqlalchemy.dialects', 'sqlalchemy.types', 'sqlalchemy.schema', 'sqlalchemy.engine.default', 'sqlalchemy.engine.reflection']

一旦从磁盘加载了一个模块并将其添加到该结构中,Python 就不需要再次加载它。点在层次结构中分隔模块名称,因此以sqlalchemy. 开头的所有内容都作为树结构存在于sqlalchemy 包中。这里有很多sqlalchemy的模块,这是一个很大的项目,都是被root package module, sqlalchemy/__init__.py加载(直接或间接)的。

import 所做的另一件事是在您当前的命名空间中绑定一个名称。每个模块都是一个“全局”命名空间,该命名空间中的所有名称在该命名空间中都是可见的。您的 Python 脚本作为 __main__ 命名空间导入,并且其中的所有名称都可用于您的脚本。如果您创建一个模块foo,那么这是一个具有自己名称的单独命名空间。 import 将名称从另一个模块添加到您的全局命名空间。而在 Python 中,名称只是引用;这些名称中的每一个引用的实际对象都存在于内存中的一大堆中,称为 heap

线

from sqlalchemy import create_engine

首先确保对象sys.modules['sqlalchemy'] 存在,并将名称create_engine 添加到您当前的命名空间,对sqlalchemy.create_engine 的引用,就像执行了create_engine = sys.modules['sqlalchemy'].create_engine 行一样:

>>> sys.modules['sqlalchemy'].create_engine
<function create_engine at 0x10188bbf8>
>>> from sqlalchemy import create_engine
>>> create_engine is sys.modules['sqlalchemy'].create_engine
True

同样,Python 中的所有名称​​只是对内存中一大堆对象的引用

当您调用create_engine() 函数时,该函数的代码将被执行,并且该函数可以访问其定义所在命名空间中的所有全局变量。在这种情况下,该函数在sqlalchemy.engine 模块中定义(顶层sqlalchemy 模块本身已将其导入为from sqlalchemy.engine import create_engine,因此您可以从更方便的位置访问它):

>>> create_engine.__module__
'sqlalchemy.engine'
>>> sys.modules['sqlalchemy.engine']
<module 'sqlalchemy.engine' from '/Users/mjpieters/Development/venvs/stackoverflow-3.6/lib/python3.6/site-packages/sqlalchemy/engine/__init__.py'>
>>> sorted(vars(sys.modules['sqlalchemy.engine']))
['BaseRowProxy', 'BufferedColumnResultProxy', 'BufferedColumnRow', 'BufferedRowResultProxy', 'Compiled', 'Connectable', 'Connection', 'CreateEnginePlugin', 'Dialect', 'Engine', 'ExceptionContext', 'ExecutionContext', 'FullyBufferedResultProxy', 'NestedTransaction', 'ResultProxy', 'RootTransaction', 'RowProxy', 'Transaction', 'TwoPhaseTransaction', 'TypeCompiler', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'base', 'connection_memoize', 'create_engine', 'ddl', 'default', 'default_strategy', 'engine_from_config', 'interfaces', 'reflection', 'result', 'strategies', 'threadlocal', 'url', 'util']

该名称列表是在定义create_engine 的同一模块中定义的所有名称。该模块已由您导入sqlalchemy 模块时执行的代码加载。该函数可以访问所有这些对象,并且可以返回任何此类对象。您会注意到那里定义了 Engine 名称:

>>> sys.modules['sqlalchemy.engine'].Engine
<class 'sqlalchemy.engine.base.Engine'>

所以该对象已经加载到 Python 内存中。该函数所做的只是为您创建该类的一个实例并返回它:

>>> engine = create_engine('sqlite:///:memory:')
>>> engine
Engine(sqlite:///:memory:)
>>> type(engine)
<class 'sqlalchemy.engine.base.Engine'>

如果您想了解更多有关 Python 和名称的信息,我建议您阅读 Ned Batchelder 在 Facts and myths about Python names and values 上的文章。

【讨论】:

  • 所以我是否正确理解 from x import y: (1) 使用 sys.modules 使 x(以及 x 中包含的所有内容)可供我使用,如果 x 尚不存在,独立于你是什​​么。 (2) 只是给 x.y 一个易于使用的名称,以便我在我的脚本中使用
  • @JorenSips:是的,and 将一个或多个名称添加到您当前的命名空间。所以import foobar 确保foobar 被加载(只需要一次),然后将名称foobar 添加到您当前的命名空间,它是对模块对象的引用。 from foobar import spam 将确保已加载 foobar(如果尚未加载),然后将名称 spam 添加到您当前的命名空间,这是对 foobar.spam 引用的对象的引用。您可以在这些语句中添加as ... 以调整使用的名称。
猜你喜欢
  • 2020-06-20
  • 2017-09-26
  • 2021-03-11
  • 2016-08-28
  • 2021-12-04
  • 2012-12-25
  • 1970-01-01
  • 2021-10-11
  • 2019-10-30
相关资源
最近更新 更多