摘要:前言继续向下看廖大教程,看到了函数式编程这一节,当时是觉得没啥用直接跳过了,这次准备要仔细看一遍了,并记录下一些心得。
前言
继续向下看廖大教程,看到了函数式编程这一节,当时是觉得没啥用直接跳过了,这次准备要仔细看一遍了,并记录下一些心得。
函数式编程上学期有上一门叫 "人工智能" 的课,老师强行要我们学了一个叫做 prolog 的语言,哇那感觉确实难受,思维方式完全和之前学过的不一样,写个汉诺塔想了半天,最后还是在网上找了段代码修改一下(怕被老师发现抄袭)才写出来,贴一段出来感受一下:
hanoi(N) :- dohanoi(N, "a", "b", "c"). dohanoi(0, _ , _ , _ ) :- !. dohanoi(N, A, B, C) :- N1 is N-1, dohanoi(N1, A, C, B), writeln([move, N, A-->C]), dohanoi(N1, B, A, C).
当时是差不多弄懂了,主要是资料实在太少,debug 都无从谈起,一遇上 bug 就 gg,我现在自己看也有点头晕。不过据说 prolog 当年能和 Lisp 一争高下,最近对 Lisp 也有点兴趣,等弄完这些就去参拜一下这类函数式语言。
何谓函数式编程?廖大这里写道:
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
可能看完还是有些不太理解,不急,先看完这几个小节吧。
高阶函数在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
接受一个或多个函数作为输入
输出一个函数
也就是说,把函数本身当成参数传递,或者返回一个函数。
例如,可以像普通赋值一样将函数赋值给变量:
>>> min(1, 2) 1 >>> f = min >>> f(1, 2) 1 >>> f>>> min
也可以给函数赋值(代码接上):
>>> min = 10 >>> min(1, 2) Traceback (most recent call last): File "", line 1, in TypeError: "int" object is not callable >>> f(1, 2) 1 >>> min = f >>> min(1, 2) 1
还可以传参,例如,一个计算所有数字的和的函数:
>>> def add(a, b): ... return a+b ... >>> def mysum(f, *l): ... a = 0 ... for i in l: ... a = f(a, i) ... return a ... >>> mysum(add, 1, 2, 3) 6 >>> mysum(add, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) 55
当然,将这个 f 换成乘法就是计算所有数字的乘积了。
再来看看 python 内置的一些高阶函数,经常会用到。
map/reduce记得上学期上云计算的课程时依稀有听到过这个词,不过这课很水,就没怎么听,在这里看到好像发现不太一样??
不过没啥说的,简单说一下每个函数的作用。
对于 map,其计算式可以看成这样:
map(f, [x1, x2, ..., xn]) = [f(x1), f(x2), ..., f(xn)]
对于 reduce,其计算式可以看成这样:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
廖大那里说得很清楚啦。
filterfilter 和 map 函数类似,接受一个函数和 iterable,返回也是一个 list,不过其功能是根据函数返回值是否为 True 来判断是否保留该值。例如:
def is_odd(n): return n % 2 == 1 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) # 结果: [1, 5, 9, 15]sorted
sorted 函数同样是一个高阶函数,对参数 key 传递函数可以将需要排列的序列经过 key 函数处理后再进行排序,不过不会改变序列的值,例如:
>>> sorted([36, 5, -12, 9, -21], key=abs) [5, 9, -12, -21, 36]装饰器(decorator)
匿名函数就不说了,以后用时再仔细看吧,装饰器我记得之前看 flask 的时候都研究了好久,这次再来复习一下。
简单装饰器首先是一个简单的装饰器,在每次调用函数前打印出日志:
import logging def log(func): def wrapper(*args, **kw): logging.warn("%s is running" % func.__name__) func(*args, **kw) return wrapper
这就是一个极其简单的装饰器,如何使用它呢?我最先看到的用法是在需要装饰的函数前添加@,但其实这是 Python 的一个语法糖,最原始的用法反而更能让人理解,先定义一个函数 f:
def f(): print("in function f") f = log(f)
这样定义了之后,我们再调用 f 函数:
>>> f() WARNING:root:f is running in function f
使用 @log 的结果与其一样,其实@符号作为装饰器的语法糖,与前面的赋值语句具有相同的功能,使代码看起来更简洁明了,避免再一次赋值操作,就像下面这样:
@log def f(): print("in function f")含参数的装饰器
有时候我们还需要向装饰器中传入参数,例如,状态,层次等信息,只需要在 wrapper 函数外再"包裹"一层函数,如下所示:
import logging def log(level): def decorator(func): def wrapper(*args, **kw): logging.warn("%s is running at level %d" % (func.__name__, level)) return func(*args, **kw) return wrapper return decorator @log(2) def f(): print("in function f") >>> f() WARNING:root:f is running at level 2 in function f进一步理解
为了再进一步理解装饰器,我们可以打印出函数 f 的 name 属性:
#对于不加装饰器的 f,其 name 不变 >>> def f(): ... print("in function f") ... >>> f.__name__ "f" #对于添加装饰器的函数,其 name 改变了 >>> @log ... def f(): ... print("in function f") ... >>> f.__name__ "wrapper"
联系到最前面的装饰器赋值语句,就可以大致明白发生了什么:f = log(f) 使得 f 指向修改为 log(f) 的返回值,即 wrapper 函数。每次运行原函数 f 时,则会调用 wrapper 函数,在我们这个例子中,则是先打印日志,然后运行原函数 f。
不过这样有一个问题,这样使得原函数 f 的元信息被替换了,关于 f 的许多信息消失不见,这是很难令人接受的,不过好在我们有 functools 模块,修改函数为:
import functools import logging def log(func): functools.wraps(func) def wrapper(*args, **kw): logging.warn("%s is running" % func.__name__) func(*args, **kw) return wrapper >>> @log ... def f(): ... print("in function f") ... >>> f.__name__ "f"
另外,还可以对同一个函数添加多个装饰器:
@a @b @c def f (): # 等价于 f = a(b(c(f)))总结
关于函数式编程我也不是很了解,这里只是大概了解了一下其概念吧,平时肯定还是使用命令式编程用得多。不过有语言是纯函数式语言,例如 Haskell 或 Lisp,学习它们会使得人打开一种新思路。
以上~
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/38437.html
摘要:前菜在我们使用的过程很多时候会用到运算例如输出不光在加法中使用在字符串的拼接也同样发挥这重要的作用例如输出同样的在列表中也能使用例如输出为什么上面不同的对象执行同一个会有不同的效果呢这就涉及到的重载然而这不是本文要讨论的重点上面的只是前菜而 前菜 在我们使用Python的过程, 很多时候会用到+运算, 例如: a = 1 + 2 print a # 输出 3 不光在加法中使用, 在...
摘要:环境问题发现最近刚从换到下搬砖,发现在跑的好好的代码,在下终端老是报错还是编码错误注册失败坦白说,看到这个错误好无奈。既然能在跑,换到就出错,那多半是环境问题了,然后我就开始了我的调试追踪之旅了跟踪调试先前调试都是一晃而过,只看结果。 环境:mac 10.12 python3 django 1.10 问题发现 最近刚从arch 换到 mac下搬砖, 发现在arch跑的好好的代码,...
摘要:原因在安装时,默认的编码是,当程序中出现非编码时,的处理常常会报这样的错,不过在就不会有这样的问题。 1、原因 python2.7在安装时,默认的编码是ascii,当程序中出现非ascii编码时,python的处理常常会报这样的错,不过在python3就不会有这样的问题。 2、解决办法 临时解决方法: 代码中加入如下三行import sys reload(sys) sys.setd...
摘要:起初是群里一个哥们这句话报错。我竟然没看懂代码如下其实这个代码不能很好的反应问题。来看以下两个我一开始没理解这个问题。看到和粗略的理解成不能够在闭包函数中改变上层函数的变量。实际上是,如果一个变量被赋值,那么会认为其为局部变量。 起初是群里一个哥们这句话报错。后来之前的一个实习生,给出了链接来解释这个问题。 我竟然没看懂.... http://stackoverflow.com/que...
摘要:原因很简单,因为中的代表的就是当前执行的模块名。缺点就是主程序会受待执行程序的影响,会出现待执行程序中抛异常或主动退出会导致主程序也退出的尴尬问题。总结来说就是,一个是在子进程中执行代码,一个是在当前进程中执行代码。 showImg(https://segmentfault.com/img/remote/1460000018607395?w=502&h=318); 相信刚接触Pytho...
阅读 702·2021-11-11 16:54
阅读 3030·2021-09-26 09:55
阅读 1947·2021-09-07 10:20
阅读 1170·2019-08-30 10:58
阅读 1009·2019-08-28 18:04
阅读 678·2019-08-26 13:57
阅读 3541·2019-08-26 13:45
阅读 1121·2019-08-26 11:42