请参见toolz.thread_first()
和toolz.thread_last()
。
在我看来,它们使代码变得更加糟糕。考虑
x = f(x)
x = g(x)
x = h(x)
与
x = thread_last(x,
f,
g,
h)
第一个例子是
更易读易懂,
不依赖外部Python库,
易于调试,因为多个语句都有各自的行,并且
更详细,但幅度不大。
即使您想将
x
通过带有x = thread_first(x, *funcs)
的可变大小的函数列表进行传递,也可以仅通过常规迭代来完成,这又是更冗长的,但并不是这种情况会出现反正经常都这样。为什么有人要使用
thread_first()
和thread_last()
?在我看来,这基本上是非常糟糕的风格。原则上,实现一种通过函数通过管道传递参数列表的方法可能会通过并行化来加快速度,但是在我看来,这似乎并没有发生在这些实现中。 最佳答案
虽然这主要是基于意见的,但有很多好处:
命名很困难(或者这样说),thread_*
或pipe
允许您跳过中间分配。无需发明数十个中间名称,甚至不必在x
,y
,z
变量的地狱中生活。
专注于数据流和数据结构可实现简洁的声明式样式。您的代码的大部分可以表示为简单的数据结构,并可以使用标准数据结构方法进行转换。可以说它使您的代码更容易理解:
thread_first(
url,
requests.get,
requests.models.Response.json,
operator.itemgetter("result"))
并编写/重用代码:
request_pipeline = [authorize, fetch, validate]
api_response = [render_json]
html_response = [render_html]
thread_first(request, *request_pipeline + api_response)
thread_first(request, *request_pipeline + html_response)
将焦点转移到参考透明它自然地会强制执行较小的,参考透明的功能,并且副作用(双关语)使您的代码更易于调试。
它与惰性代码(
toolz.map
,toolz.filter
)配合使用非常好,这使其非常适合在可能无限的数据结构上进行数据处理。最后,您必须记住,这些功能并不单独存在。它们旨在与
toolz
的其他部分(尤其是函数组成和currying),内置模块(例如operator
)一起使用,并且可以与第三方工具(例如multipledispatch
)一起很好地使用。只有这样,他们才能发挥出全部力量。但是,在严格的功能语言(Clojure和Elixir)中,用
toolz
实现的许多想法要自然得多,而且正如您提到的那样,对于Python开发人员来说可能并不自然。