为什么用 Python 进行函数式编程? [英] Why program functionally in Python?

查看:30
本文介绍了为什么用 Python 进行函数式编程?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在工作中,我们过去常常以非常标准的面向对象的方式编写 Python.最近,有几个人加入了功能性的潮流.他们的代码现在包含更多的 lambdas、maps 和 reduce.我知道函数式语言有利于并发,但是在函数式上编程 Python 真的有助于并发吗?我只是想了解如果我开始使用更多 Python 的功能特性,我会得到什么.

At work we used to program our Python in a pretty standard OO way. Lately, a couple guys got on the functional bandwagon. And their code now contains lots more lambdas, maps and reduces. I understand that functional languages are good for concurrency but does programming Python functionally really help with concurrency? I am just trying to understand what I get if I start using more of Python's functional features.

推荐答案

编辑:我在评论中接受了任务(部分看来,Python 中的 FP 狂热者,但不完全)因为没有提供更多解释/示例,因此,扩展答案以提供一些.

Edit: I've been taken to task in the comments (in part, it seems, by fanatics of FP in Python, but not exclusively) for not providing more explanations/examples, so, expanding the answer to supply some.

lambda,更是如此map(和filter),尤其是reduce 几乎都不是 Python 工作的正确工具,Python 是一种强大的多范式语言.

lambda, even more so map (and filter), and most especially reduce, are hardly ever the right tool for the job in Python, which is a strongly multi-paradigm language.

lambda 与普通的 def 语句相比的主要优点 (?) 是它创建了一个 匿名 函数,而 def 给函数一个名字——为了这个非常可疑的优势,你付出了巨大的代价(函数的主体仅限于一个表达式,产生的函数对象是不可pickle的,缺少名称有时会使它变得更加困难了解堆栈跟踪或以其他方式调试问题 - 我需要继续吗?!-).

lambda main advantage (?) compared to the normal def statement is that it makes an anonymous function, while def gives the function a name -- and for that very dubious advantage you pay an enormous price (the function's body is limited to one expression, the resulting function object is not pickleable, the very lack of a name sometimes makes it much harder to understand a stack trace or otherwise debug a problem -- need I go on?!-).

考虑一下您有时在Python"(带有恐吓引号"的 Python,因为它显然 不是 惯用的 Python——它是从惯用的 Scheme 或之类的,就像在 Python 中更频繁地过度使用 OOP 是 Java 之类的糟糕音译一样):

Consider what's probably the single most idiotic idiom you sometimes see used in "Python" (Python with "scare quotes", because it's obviously not idiomatic Python -- it's a bad transliteration from idiomatic Scheme or the like, just like the more frequent overuse of OOP in Python is a bad transliteration from Java or the like):

inc = lambda x: x + 1

通过将 lambda 分配给一个名称,这种方法立即抛弃了上述优点"——并且不会失去任何缺点!例如,inc知道它的名字——inc.__name__ 是一个无用的字符串 '<lambda>' -- 祝你好运,理解其中的一些堆栈跟踪;-).在这种简单的情况下,实现所需语义的正确 Python 方法当然是:

by assigning the lambda to a name, this approach immediately throws away the above-mentioned "advantage" -- and doesn't lose any of the DISadvantages! For example, inc doesn't know its name -- inc.__name__ is the useless string '<lambda>' -- good luck understanding a stack trace with a few of these;-). The proper Python way to achieve the desired semantics in this simple case is, of course:

def inc(x): return x + 1

现在 inc.__name__ 是字符串 'inc',显然应该是这样,并且对象是可腌制的——语义在其他方面是相同的(在这种简单的情况下,所需的功能很适合简单的表达式——如果您需要临时或永久插入诸如 print 之类的语句,def 也使重构变得非常容易raise,当然).

Now inc.__name__ is the string 'inc', as it clearly should be, and the object is pickleable -- the semantics are otherwise identical (in this simple case where the desired functionality fits comfortably in a simple expression -- def also makes it trivially easy to refactor if you need to temporarily or permanently insert statements such as print or raise, of course).

lambda 是表达式的(一部分)而 def 是语句的(一部分)——这是让人们使用 的一点语法糖lambda 有时.许多 FP 爱好者(就像许多 OOP 和程序爱好者一样)不喜欢 Python 在表达式和语句之间相当强的区别(对 命令-查询分离).我,我认为当你使用一种语言时,你最好顺其自然"地使用它——它设计的使用方式——而不是与之抗争;所以我以 Pythonic 的方式编写 Python,以 Schematic (;-) 的方式编写 Scheme,以 Fortesque (?) 的方式编写 Fortran,等等:-).

lambda is (part of) an expression while def is (part of) a statement -- that's the one bit of syntax sugar that makes people use lambda sometimes. Many FP enthusiasts (just as many OOP and procedural fans) dislike Python's reasonably strong distinction between expressions and statements (part of a general stance towards Command-Query Separation). Me, I think that when you use a language you're best off using it "with the grain" -- the way it was designed to be used -- rather than fighting against it; so I program Python in a Pythonic way, Scheme in a Schematic (;-) way, Fortran in a Fortesque (?) way, and so on:-).

转至 reduce —— 一个评论声称 reduce 是计算列表乘积的最佳方法.真的吗?让我们看看...:

Moving on to reduce -- one comment claims that reduce is the best way to compute the product of a list. Oh, really? Let's see...:

$ python -mtimeit -s'L=range(12,52)' 'reduce(lambda x,y: x*y, L, 1)'
100000 loops, best of 3: 18.3 usec per loop
$ python -mtimeit -s'L=range(12,52)' 'p=1' 'for x in L: p*=x'
100000 loops, best of 3: 10.5 usec per loop

所以简单的、基本的、琐碎的循环比执行任务的最佳方式"快两倍(而且更简洁)?-)我猜速度和简洁的优势因此必须使琐碎循环最好"的方式,对吧?-)

so the simple, elementary, trivial loop is about twice as fast (as well as more concise) than the "best way" to perform the task?-) I guess the advantages of speed and conciseness must therefore make the trivial loop the "bestest" way, right?-)

通过进一步牺牲紧凑性和可读性...:

By further sacrificing compactness and readability...:

$ python -mtimeit -s'import operator; L=range(12,52)' 'reduce(operator.mul, L, 1)'
100000 loops, best of 3: 10.7 usec per loop

...我们可以几乎回到最简单、最明显、紧凑和可读的方法(简单、基本、琐碎的循环)的容易获得的性能.这指出了 lambda 的另一个问题,实际上是:性能!对于足够简单的操作,例如乘法,与正在执行的实际操作相比,函数调用的开销相当大——reduce(以及 mapfilter) 通常会迫使您在简单循环、列表推导式和生成器表达式中插入这样一个函数调用,以实现内联操作的可读性、紧凑性和速度.

...we can get almost back to the easily obtained performance of the simplest and most obvious, compact, and readable approach (the simple, elementary, trivial loop). This points out another problem with lambda, actually: performance! For sufficiently simple operations, such as multiplication, the overhead of a function call is quite significant compared to the actual operation being performed -- reduce (and map and filter) often forces you to insert such a function call where simple loops, list comprehensions, and generator expressions, allow the readability, compactness, and speed of in-line operations.

也许比上述将 lambda 分配给名称"更糟糕的反习语实际上是以下反习语,例如按长度对字符串列表进行排序:

Perhaps even worse than the above-berated "assign a lambda to a name" anti-idiom is actually the following anti-idiom, e.g. to sort a list of strings by their lengths:

thelist.sort(key=lambda s: len(s))

而不是显而易见的、可读的、紧凑的、更快的

instead of the obvious, readable, compact, speedier

thelist.sort(key=len)

在这里,lambda 的使用只是插入了一个间接层——没有任何好的效果,还有很多不好的效果.

Here, the use of lambda is doing nothing but inserting a level of indirection -- with no good effect whatsoever, and plenty of bad ones.

使用 lambda 的动机通常是允许使用 mapfilter 而不是一个非常可取的循环或列表理解让您按顺序进行简单、正常的计算;当然,您仍然需要支付间接级别".不得不怀疑我应该在这里使用 listcomp 还是 map"并不是 Pythonic:只要始终使用 listcomp,当两者都适用并且您不知道选择哪个时,基于应该有一个,并且最好只有一种明显的做事方式".您经常会编写无法合理转换为映射的列表复合(嵌套循环、if 子句等),而不会调用 map明智地改写为 listcomp.

The motivation for using lambda is often to allow the use of map and filter instead of a vastly preferable loop or list comprehension that would let you do plain, normal computations in line; you still pay that "level of indirection", of course. It's not Pythonic to have to wonder "should I use a listcomp or a map here": just always use listcomps, when both appear applicable and you don't know which one to choose, on the basis of "there should be one, and preferably only one, obvious way to do something". You'll often write listcomps that could not be sensibly translated to a map (nested loops, if clauses, etc), while there's no call to map that can't be sensibly rewritten as a listcomp.

Python 中完美的函数式方法通常包括列表推导式、生成器表达式、itertools、高阶函数、各种伪装的一阶函数、闭包、生成器(偶尔还有其他类型的迭代器).

Perfectly proper functional approaches in Python often include list comprehensions, generator expressions, itertools, higher-order functions, first-order functions in various guises, closures, generators (and occasionally other kinds of iterators).

itertools,正如评论者指出的,确实包括 imapifilter:区别在于,像所有 itertools 一样,它们是基于流(如 Python 3 中的 mapfilter 内置函数,但与 Python 2 中的内置函数不同).itertools 提供了一组相互组合良好的构建块,并具有出色的性能:特别是如果您发现自己可能处理很长(甚至无限!-)的序列,您应该归功于自己熟悉 itertools —— 文档中的整个章节很适合阅读,并且食谱尤其具有指导意义.

itertools, as a commenter pointed out, does include imap and ifilter: the difference is that, like all of itertools, these are stream-based (like map and filter builtins in Python 3, but differently from those builtins in Python 2). itertools offers a set of building blocks that compose well with each other, and splendid performance: especially if you find yourself potentially dealing with very long (or even unbounded!-) sequences, you owe it to yourself to become familiar with itertools -- their whole chapter in the docs makes for good reading, and the recipes in particular are quite instructive.

编写自己的高阶函数通常很有用,尤其是当它们适合用作 装饰器(函数装饰器,如文档的那部分所述,和类装饰器,在 Python 2.6 中引入).请记住在函数装饰器上使用 functools.wraps(保持函数的元数据被包装)!

Writing your own higher-order functions is often useful, especially when they're suitable for use as decorators (both function decorators, as explained in that part of the docs, and class decorators, introduced in Python 2.6). Do remember to use functools.wraps on your function decorators (to keep the metadata of the function getting wrapped)!

所以,总结...:任何你可以用 lambdamapfilter 编码的东西,你都可以编码(比不利于)与 def(命名函数)和 listcomps —— 通常向上移动一个等级到生成器、生成器表达式或 itertools,甚至更好.reduce 符合有吸引力的滋扰"的法律定义......:它几乎永远是适合这项工作的工具(这就是为什么它不再是 Python 中的内置工具3,终于!-).

So, summarizing...: anything you can code with lambda, map, and filter, you can code (more often than not advantageously) with def (named functions) and listcomps -- and usually moving up one notch to generators, generator expressions, or itertools, is even better. reduce meets the legal definition of "attractive nuisance"...: it's hardly ever the right tool for the job (that's why it's not a built-in any more in Python 3, at long last!-).

这篇关于为什么用 Python 进行函数式编程?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆