Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Luigi:如何将不同的参数传递给叶任务?_Python_Etl_Pipeline_Luigi - Fatal编程技术网

Python Luigi:如何将不同的参数传递给叶任务?

Python Luigi:如何将不同的参数传递给叶任务?,python,etl,pipeline,luigi,Python,Etl,Pipeline,Luigi,这是我第二次尝试理解如何在Luigi中将参数传递给依赖项。第一个是 想法是:我有TaskC,它依赖于TaskB,它依赖于TaskA,它依赖于Task0。我希望整个序列始终完全相同,只是我希望能够控制从哪个文件Task0读取,让我们称之为path。Luigi的理念通常是,每个任务应该只知道它所依赖的任务及其参数。问题是TaskC、TaskB和TaskA都必须接受变量path,唯一目的是将其传递给Task0 因此,Luigi为此提供的解决方案称为 下面是一些示例代码: from pathlib im

这是我第二次尝试理解如何在Luigi中将参数传递给依赖项。第一个是

想法是:我有
TaskC
,它依赖于
TaskB
,它依赖于
TaskA
,它依赖于
Task0
。我希望整个序列始终完全相同,只是我希望能够控制从哪个文件
Task0
读取,让我们称之为
path
。Luigi的理念通常是,每个任务应该只知道它所依赖的任务及其参数。问题是
TaskC
TaskB
TaskA
都必须接受变量
path
,唯一目的是将其传递给
Task0

因此,Luigi为此提供的解决方案称为

下面是一些示例代码:

from pathlib import Path
import luigi
from luigi import Task, TaskParameter, IntParameter, LocalTarget, Parameter

class config(luigi.Config):
    path = Parameter(default="defaultpath.txt")

class Task0(Task):
    path = Parameter(default=config.path)
    arg = IntParameter(default=0)
    def run(self):
        print(f"READING FROM {self.path}")
        Path(self.output().path).touch()
    def output(self): return LocalTarget(f"task0{self.arg}.txt")

class TaskA(Task):
    arg = IntParameter(default=0)
    def requires(self): return Task0(arg=self.arg)
    def run(self): Path(self.output().path).touch()
    def output(self): return LocalTarget(f"taskA{self.arg}.txt")

class TaskB(Task):
    arg = IntParameter(default=0)
    def requires(self): return TaskA(arg=self.arg)
    def run(self): Path(self.output().path).touch()
    def output(self): return LocalTarget(f"taskB{self.arg}.txt")

class TaskC(Task):
    arg = IntParameter(default=0)
    def requires(self): return TaskB(arg=self.arg)
    def run(self): Path(self.output().path).touch()
    def output(self): return LocalTarget(f"taskC{self.arg}.txt")
(忽略所有
输出
运行
内容。它们就在那里,因此示例可以成功运行。)

上面示例的要点是控制行
print(f“READING FROM{self.path}”)
,而不让任务A、B、C依赖于
path

实际上,通过配置类,我可以控制
Task0
参数。如果未向
Task0
传递
path
参数,它将采用默认值,即
config().path

我现在的问题是,在我看来,这只在“构建时”(解释器第一次加载代码时)起作用,而不是在运行时(细节我不清楚)

因此,这两种方法都不起作用:

(A)

我不知道为什么这不起作用

(B)

这实际上是有道理的。有两个
config
类,我只设法更改了其中一个的
路径

帮忙

编辑:当然,让
path
引用一个全局变量是可行的,但它不是通常意义上的参数

编辑2:我试过下面答案的第1点:

config
具有相同的定义

class config(luigi.Config):
    path = Parameter(default="defaultpath.txt")
我修复了指出的错误,即任务0现在是:

class Task0(Task):
    path = Parameter(default=config().path)
    arg = IntParameter(default=0)
    def run(self):
        print(f"READING FROM {self.path}")
        Path(self.output().path).touch()
    def output(self): return LocalTarget(f"task0{self.arg}.txt")
最后我做到了:

if __name__ == "__main__":
    for i in range(3):
        config.path = Parameter(f"file_{i}")
        luigi.build([TaskC(arg=i)], log_level="WARNING")

这不起作用,
Task0
仍然会得到
path=“defaultpath.txt”

,因此您尝试使用参数创建任务,而不将这些参数传递给父类。这是完全可以理解的,在试图处理这件事时,我有时会感到恼火

首先,您错误地使用了
config
类。使用配置类时,如中所述,需要实例化对象。因此,不是:

class Task0(Task):
    path = Parameter(default=config.path)
    ...
您可以使用:

class Task0(Task):
    path = Parameter(default=config().path)
    ...
虽然现在可以确保使用的是值而不是
参数
对象,但它仍然不能解决问题。创建类
Task0
时,将计算
config().path
,因此它不会将
config().path
的引用分配给
path
,而是在调用时分配值(该值将始终为
defaultpath.txt
)。以正确的方式使用该类时,luigi将构造一个
Task
对象,其中只有
luigi.Parameter
属性作为新实例上的属性名称,如下所示:

因此,我看到了两条可能的前进道路

1.)第一种方法是在运行时像以前一样设置配置路径,只是将其设置为
参数
对象,如下所示:

config.path = luigi.Parameter(f"newpath_{i}")
但是,使用
config.path
让您的任务工作起来需要做很多工作,因为现在它们需要以不同的方式接受其参数(在创建类时无法计算默认值)

2.)更简单的方法是在配置文件中简单地指定类的参数。如果你看一下,你会发现Luigi中的
Config
类实际上只是一个
Task
类,所以你可以用它做任何事情,你可以用类做任何事情,反之亦然。因此,您可以在配置文件中包含以下内容:

[Task0]
path = newpath_1
...
3.)但是,由于您似乎希望运行多个任务,每个任务都有不同的参数,因此我建议您按照Luigi的建议,将args传递给家长。然后,您可以使用以下工具运行所有内容:

luigi.build([TaskC(arg=i) for i in range(3)])
4.)最后,如果确实需要消除传递的依赖项,可以创建一个
ParamaterizedTaskParameter
,该参数扩展
luigi.ObjectParameter
,并使用任务实例的pickle作为对象

在上述解决方案中,我强烈建议2个或3个。1将很难编程,4将创建一些非常难看的参数,并且更高级


编辑:解决方案1和解决方案2比任何东西都要复杂,建议您将参数捆绑在
DictParameter

2)中,就我所知,它不会在循环中工作,对吧?。3)
arg
是我的第二个问题,它只是一个例子。真正的问题是将
path
传递给leaf任务。1) 我真的不明白。在您的示例中,配置仍然是一项任务吗?那么,我是不是不应该执行
config().path
实例化?2在循环中不起作用是正确的。在3中,我一般使用arg。这可能是你的道路。关键是,您将循环该参数的所有可能值,并使用每个可能值创建一个TaskC,并传入该参数。您不应该执行
config().path
实例化。它违背了Luigi的工作方式,不适用于您的用例。好吧,但我的观点是,我不希望所有任务都知道必须传递给
Task0
的路径。如果我有100个任务呢?我是否必须向每个人添加他们不依赖的内容,以便他们可以将其传递给下一个?这似乎是一个巨大的设计缺陷。必须有一个解决方案,这不是一个模糊的要求,对吗?顺便说一句,我还编辑了原始问题,其中我描述了我无法让选项1)工作(或
[Task0]
path = newpath_1
...
luigi.build([TaskC(arg=i) for i in range(3)])