为什么要在Python中进行功能编程? [英] Why program functionally in Python?

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

问题描述

在工作中,我们用一种非常标准的OO方式来编写我们的Python。最近,几个人开始了功能性的潮流。他们的代码现在包含更多的lambda表达式,映射和缩减。我知道函数式语言对并发有好处,但是编程Python在功能上确实有助于并发?我只是想了解如果我开始使用更多Python的功能特性,我会得到什么。

strong>:我已经接受了评论中的任务(部分原因,似乎是由Python中的FP狂热分子,但不是唯一的),因为没有提供更多的解释/例子,所以扩展答案以提供一些答案。 p>

lambda ,甚至更多 map (和 filter> ),,特别是 reduce 几乎不是Python中作业的正确工具,这是一种强烈的多范式语言。

lambda 主要优势(?)与普通<$ c相比$ c> def 语句是它使一个匿名函数,而 def 给这个函数一个名字,对于那种非常可疑的优势,你付出了巨大的代价(函数的主体仅限于一个表达式,所产生的函数对象不是泡菜能够,非常缺乏名字有时会使得理解堆栈跟踪或以其他方式调试问题变得更加困难 - 需要我继续下去?! - )。

考虑什么可能是你在Python中使用的最愚蠢的成语(Python带有恐吓引语,因为它明显是不是惯用的Python-它是一种不习惯性的Scheme或类似的音译,只是就像在Python中过度使用OOP是Java等的不好的译音一样):

  inc = lambda x:x + 1 

通过将lambda分配给名称,这种方法立即抛弃了上述优势 - 并且不会失去任何缺点!例如, inc 不知道其名称 inc .__ name __ 是无用的字符串'< lambda>' - 祝您好运,了解堆栈跟踪以及其中的几个;-)。当然,在这种简单的情况下,实现所需语义的合适的Python方法是:

  def inc(x):return x + 1 

现在 inc .__ name __ 是字符串'inc',因为它显然应该是这样的,并且对象可以被pickleable - 语义在其他方面是相同的(在这种简单的情况下,如果你需要暂时或永久地插入诸如 print 这样的简单表达式,那么所需的功能也可以轻松地进行简单的重构: def / code>或 raise ,当然)。

lambda code>是(表达式的一部分),而 def 是语句的一部分 - 这是语法糖的一点,它使人们使用 lambda 有时。许多FP爱好者(就像许多OOP和程序迷一样)不喜欢Python在表达式和语句之间进行相当强的区分(对于 Command-Query Separation )。我认为,当你使用一种语言时,你最好使用它与谷物 - 它被设计成被使用的方式 - 而不是与它作战;所以我以Pythonic的方式编程Python,Scheme以Schematic(;-)方式编程,Fortran以Fortesque(?)方式编写,等等: - )。

转到 reduce - 一条评论声称 reduce 是计算列表产品的最佳方式。真的吗?让我们来看看......:

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $''$' x,y:x * y,L,1)'
100000循环,最好是3:每循环18.3 usec
$ python -mtimeit -s'L = range(12,52)''p = 1''for x in L:p * = x'
100000个循环,最好是3:每个循环10.5个usec

所以简单的,基本的,平凡的循环比执行任务的最好的方式快两倍(以及更简洁)?)我认为速度和简洁的优点必须因此,让平凡的循环成为最好的方式吧? -

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

  $ python -mtimeit -s'importport operator; L =范围(12,52)''reduce(operator.mul,L,1)'
100000循环,最好的3:每循环10.7个usec

...我们可以将几乎转换回最简单,最明显,紧凑和易读的方法(简单,基本的,平凡的循环)。这指出了 lambda 的另一个问题,实际上:performance!对于充分简单的操作,例如乘法,函数调用的开销与正在执行的实际操作相比相当重要 - reduce (和 map filter )通常会强制您在简单循环,列表推导和生成器表达式中插入此类函数调用,从而实现可读性,紧凑性和速度在线操作。

也许比上面所说的更糟糕的是将lambda命名为名称反成语实际上是以下反成语,例如按长度对字符串列表进行排序:

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

而不是明显的,可读的,紧凑的,更快的

< pre $ thelist.sort(key = len)

使用 lambda 没有作用,只是插入了一个间接的级别 - 没有什么好效果,还有很多坏的。



使用 lambda 的动机通常允许使用 map filter ,而不是一个非常可取的循环或列表理解,它可以让你在行中进行普通的正常计算;当然,你仍然支付间接水平。不应该想知道我应该在这里使用listcomp还是map:只要总是使用listcomps,当两者都适用,并且你不知道应该选择哪一个时,应该以应该有一个为基础,并且最好只有一个,明显的做某事的方式。您经常会编写无法明智地转换为地图的listcomps(嵌套循环, if 子句等),而不会调用 map

不能被合理地重写为listcomp。



Python中完美正确的函数式方法通常包括列表解析,生成器表达式,<$ c

$ itertools
,高阶函数,各种伪装,闭包,生成器的一阶函数(偶尔还有其他类型的迭代器)。

itertools ,正如一位评论者指出的那样,包括 imap ifilter :区别在于,像所有itertools一样,这些都是基于流的(比如 map filter builtins in Python 3,但与Python 2中的内建函数不同)。 itertools 提供了一组构建模块,它们彼此组合良好,并且表现出色:特别是如果您发现自己有可能处理很长(甚至是无界的)序列,您应该熟悉itertools - 文档中的整个一章为了获得良好的阅读效果,尤其是食谱非常有启发性。

编写自己的高阶函数通常很有用,特别是当它们适合用作装饰器(这两个函数装饰器,在文档的部分和类装饰器中解释,在Python 2.6中引入)。请记得在你的函数装饰器上使用 functools.wraps (以保持函数的元数据被包装)!

因此,总结......:任何你可以用 lambda map filter ,您可以使用<$ c $代码c> def (命名函数)和listcomps - 并且通常向生成器,生成器表达式或 itertools 移动一个陷波器甚至更好。 reduce 符合有吸引力的滋扰的法律定义......:它几乎没有适用于工作的正确工具(这就是为什么它不是建造 - 再次在Python 3中,终于! - )。


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.

解决方案

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, 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 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?!-).

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

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

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 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:-).

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

...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.

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)

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

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.

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, 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.

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)!

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天全站免登陆