Dagster运行在Python上,大多数对Python有基本了解的数据工程师或开发人员都可以快速启动并运行简单的管道。但是一些不太熟悉Python的用户发现Python包有点令人头疼。
因此,让我们谈谈什么是 Python 包以及如何使用它们。我们将介绍特定主题,这些主题将帮助你了解构建 Python 项目所涉及的内容,以及这如何转换为更复杂的生成,例如数据管道和业务流程协调程序。在后面的文章中,我们将看到这些概念如何应用于Dagster。
如果您只使用过现有的代码库或 Jupyter 笔记本,那么从头开始打包代码可能会让人不知所措。什么是文件,何时应使用它?什么是相对进口与绝对进口?让我们潜入!__init__.py
我们将 Python 代码放入包中,因为它可以轻松地在 Python 社区中共享和重用代码。包只是文件和目录的集合,其中包括我们稍后将检查的代码、文档和其他必要文件。
当我们想要重用复杂的代码时,我们使用 Python 包而不是脚本文件和 Jupyter 笔记本。使用脚本文件,代码可能会变得混乱且难以维护,而笔记本通常用于探索性工作,但不容易重用。
您可以将 Python 包视为一个独立的“项目”。一个项目可以包含多个模块,每个模块都包含一组特定的相关函数和变量。因此,这使您可以更轻松地将所需“项目”中的工具嵌入到您自己的代码中。
模块是 Python 包的构建块。模块是包含定义和语句的单个 Python 文件。它们提供了一种将代码结构化为逻辑单元并在多个项目中重用代码的方法。
若要在代码中使用模块,请使用 import 语句。例如,如果您有一个名为 的模块,则可以通过以下 import 语句在代码中使用它的函数和变量:mymodule.py
import mymodule
导入模块后,可以使用点 () 表示法访问其函数和变量。例如,如果 mymodule.py 文件有一个名为 的函数,则可以在代码中使用它,如下所示:.
greet
import mymodule
mymodule.greet("John")
让我们创建自己的示例模块来说明这个概念。创建一个名为的文件,并向其中添加以下代码:examplemodule.py
def greet(name):
print("Hello, " + name + "!")
def add(a, b):
return a + b
在这里,我们在文件中定义了两个函数和 。现在可以导入这些函数并在代码的其他部分中使用。greet
add
examplemodule.py
随着代码的增长,在单个模块中管理和维护所有代码可能会变得困难。包提供了一种将代码组织和拆分为多个模块的方法,同时仍保持所有内容井井有条且可访问。
要创建包,只需创建一个目录并在其中放置一个或多个模块。该目录应包含一个名为 的特殊文件,该文件告诉 Python 此目录是一个包,应按包处理。该文件可以留空,也可以包含导入包时将执行的代码。我们将在下面更详细地解释文件。__init__.py
__init__.py
__init__.py
让我们将上一节中的示例模块重构为包。创建一个名为的目录并将文件移动到其中。然后,创建一个在目录中调用的文件。examplepackage
examplemodule.py
__init__.py
examplepackage
您的文件结构现在应如下所示:
examplepackage/
__init__.py
examplemodule.py
现在,您可以在代码中从文件中导入函数,如下所示:examplemodule.py
import examplepackage.examplemodule
examplepackage.examplemodule.greet("John")
examplepackage.examplemodule.add(1, 2)
在此示例中,我们已将文件重构为名为 的包。文件中的函数现在可以像以前一样导入并在代码中使用,但具有包提供的组织和模块化的额外好处。examplemodule.py
examplepackage
examplemodule.py
__init__.py
是 Python 包中的一个特殊文件,用作包的入口点。它在导入包时执行,其代码可用于初始化包或设置任何必要的组件。该文件是可选的,但通常用于定义包的公共接口,使其他开发人员更容易理解和使用包。
在以前版本的 Python 中,需要将目录识别为包。但是,从 Python 3.3 开始,由于引入了 PEP 420,它是可选的,它允许在没有文件的情况下定义包。__init__.py
__init__.py
__init__.py
下面是如何在包中使用的示例:__init__.py
# examplepackage/__init__.py
from .examplemodule import greet, add
__all__ = [
'greet',
'add',
]
在此示例中,该文件从文件导入 和 函数,并使它们成为包的公共接口的一部分。该变量用于定义包的公共接口,并使其他开发人员更容易理解和使用包。__init__.py
greet
add
examplemodule.py
__all__
通过此设置,您现在可以从 和 函数导入,如下所示:greet
add
examplepackage
import examplepackage
examplepackage.greet("John")
examplepackage.add(1, 2)
开发人员分发包的最常见方式是将它们上传到称为 Python 包索引 (PyPI) 的公共存储库。我们使用一个名为pip的系统,它代表“Pip安装包”。它是一个命令行工具,允许用户从 PyPI 和其他包索引安装和管理包。
如果您使用 ,则已通过 Python 包索引 (PyPI) 下载并安装了包。pip install
像 pip 这样的包管理系统可以轻松安装、更新和删除包,以及管理项目中的依赖项(其他包正常运行所需的包)。
pip install
是我们用来从名为 PyPI 的库甚至您自己的计算机下载和安装不同包的命令。当您运行此命令时,它将检查该软件包在 PyPI 上是否可用,如果是,它将下载并将其安装在您的计算机上。此外,它将检查 - 如果需要,安装- 包元数据中列出的所有依赖项。最后,pip 将跟踪您安装的所有软件包,以帮助您以后升级或卸载它们。
默认情况下, 会安装最新版本的软件包,但如果需要,可以选择使用 来安装特定版本,例如,可以使用 。如果你的代码出现问题并且需要使用特定版本的包,这会很有帮助。pip install
pip install
pip install numpy==1.23.5
你有没有注意到,当你用来向你的Python代码添加一个功能时,你用来安装它的名称与你导入它时使用的名称不同?发生这种情况是因为有两种类型的名称:pip install
pip install
发行版名称是唯一的,并且通过 PyPI(从中获取包的库)保证与其他包名称不同。另一方面,包名称由创建包的人选择,因此它可能不是唯一的。
这就是为什么您可以使用 安装名为“dagster-dbt”的包,但使用名称“dagster_dbt”将其导入代码中。这也是为什么你可以使用 来安装一个名为“scikit-learn”的包,但使用名称“sklearn”将其导入到你的代码中。pip install
pip install
编写包时,有时我们可能希望从同一包中的另一个模块导入代码。我们需要在 Python 中导入模块或包的两种不同方式之间进行选择,相对导入或绝对导入。
相对导入由显式或隐式导入组成,但您实际上只需要了解显式相对导入,因为 Python 3 不支持隐式相对导入。
相对导入允许您导入相对于当前模块的模块。它们使用关键字“from”,后跟当前包的名称以及要导入的模块或包的名称。例如,如果有一个以两个模块命名的包,并且 可以使用相对导入将代码从 导入 ,如下所示:examplepackage
module1.py
module2.py
module1.py
module2.py
# examplepackage/module2.py
from .module1 import greeting
def greet(name):
print(greeting + " " + name)
在这里,相对导入用于将变量从文件导入到文件中。前面指示导入应相对于当前模块。from .module1 import greeting
greeting
module1.py
module2.py
.
module1
绝对导入允许您使用模块的全名导入模块,而不管它们相对于当前模块的位置如何。它们使用要导入的模块或包的完整路径。例如,您可以使用绝对导入将变量从文件转移到文件中,如下所示:greeting
module1.py
module2.py
# examplepackage/module2.py
from examplepackage.module1 import greeting
def greet(name):
print(greeting + " " + name)
在这里,绝对导入用于将问候变量从文件导入到文件中。模块的全名 用于指定模块的位置。from examplepackage.module1 import greeting
module1.py
module2.py
examplepackage.module1
在 Python 3 中,相对导入必须是显式的,绝对导入是默认行为。