python:函数的高级特性
很多语言中,都允许把函数本身做为参数,传递给其它参数:即所谓的高阶函数。python中也有类似特性:
一、map/reduce、filter、sorted
def fn_map(x):
print("fn_map->", x)
return 10 * x L = [3, 4, 6, 8] print(list(map(fn_map, L)))
print("\n")
输出:
fn_map-> 3
fn_map-> 4
fn_map-> 6
fn_map-> 8
[30, 40, 60, 80]
结合map,我们再把reduce函数加上(最终效果:将所有元素*10再平方,最终得出 “平方和”的"平方根")
def fn_sqrt(x, y):
print("fn_sqrt->", x, ",", y)
return math.sqrt(x ** 2 + y ** 2) def fn_map(x):
print("fn_map->", x)
return 10 * x L = [3, 4, 6, 8] result = reduce(fn_sqrt, map(fn_map, L))
print(result) print("\n")
print(math.sqrt((3 * 10) ** 2 + (4 * 10) ** 2 + (6 * 10) ** 2 + (8 * 10) ** 2))
注:要先import math,上面的代码输出如下:
fn_map-> 3
fn_map-> 4
fn_sqrt-> 30 , 40
fn_map-> 6
fn_sqrt-> 50.0 , 60
fn_map-> 8
fn_sqrt-> 78.10249675906654 , 80
111.80339887498948 111.80339887498948
上面这个例子,可能实用性不大,下面给个实用性更强的示例,将每个单词的首字母大写,其它字母变小写。
def normalize(name):
return name[:1].upper() + name[1:].lower() L1 = ['adam', 'LISA', 'barT']
print(list(map(normalize, L1)))
输出:
1.2 filter
filter跟java8里的stream的filter是类似的,可以实现对集合中的元素,按某种规则进行筛选。
示例1:找出10以内的偶数
result = filter(lambda x: x % 2 == 0, range(1, 11))
print(list(result)) # 上面的写法,等效于下面这个
def even(x):
return x % 2 == 0 print(list(filter(even, range(1, 11))))
输出:
[2, 4, 6, 8, 10]
[2, 4, 6, 8, 10]
示例2:找出200以内的"回数"(即:从左向右,从右向左,都是一样的数,比如:131, 141)
def is_palindrome1(n):
if n < 10:
return True
s = str(n)
for i in range(0, int(len(s) / 2)):
if s[i] == s[-i - 1]:
return True
return False def is_palindrome2(n):
s1 = str(n)
s2 = list(reversed(s1))
return list(s1) == s2 print(list(filter(is_palindrome1, range(1, 201))))
print(list(filter(is_palindrome2, range(1, 201))))
输出:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]
1.3 sorted
python内置的排序函数sorted,支持数字/字母/以及复杂对象排序,默认是从小到大排序,对于复杂对象的排序规则可以开发者自定义。参考下面的示例:
origin = [-1, 3, -5, 2, -4, 6]
# 从小到大排序
a = sorted(origin)
print(a) # 按abs绝对值,从小大到排序
a = sorted(origin, key=abs)
print(a) # 从大到小排序
a = sorted(origin, reverse=True)
print(a) origin = ["Xy", "Aa", "Bb", "dd", "cC", "aA", "Zo"] # 按字母ascii值从小到大排序
print(sorted(origin)) # 将字母转大写后的值排序(即:忽略大小写)
print(sorted(origin, key=str.upper)) # 将字母转大写后的值倒排序
print(sorted(origin, key=str.upper, reverse=True)) # 复杂对象排序
origin = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)] def by_name(t):
return t[0] # 按人名排序
print(sorted(origin, key=by_name)) def by_score(t):
return t[1] # 按得分倒排
print(sorted(origin, key=by_score, reverse=True))
输出:
[-5, -4, -1, 2, 3, 6]
[-1, 2, 3, -4, -5, 6]
[6, 3, 2, -1, -4, -5]
['Aa', 'Bb', 'Xy', 'Zo', 'aA', 'cC', 'dd']
['Aa', 'aA', 'Bb', 'cC', 'dd', 'Xy', 'Zo']
['Zo', 'Xy', 'dd', 'cC', 'Bb', 'Aa', 'aA']
[('Adam', 92), ('Bart', 66), ('Bob', 75), ('Lisa', 88)]
[('Adam', 92), ('Lisa', 88), ('Bob', 75), ('Bart', 66)]
二、延迟计算/闭包
python的函数定义可以嵌套(即:函数内部再定义函数),利用这个特性很容易实现延迟计算:
import time def export1(month):
print("export1 month:", month, " doing...")
time.sleep(5)
print("export1 done!") def export2(month):
def do():
print("export2 month:", month, " doing...")
time.sleep(5)
print("export2 done!") return do export1(10) print("----------------") r2 = export2(10)
print(r2)
r2()
这里我们模拟一个耗时的导出功能(假设:要求传入月份,然后导出该月的报表数据),export1为常规版本,调用export1就会马上执行。而export2则是返回一个内部函数do(),调用export2后,返回的是一个Function,并没有实际执行(可以理解为: 返回的是业务处理算法,而非处理结果),真正需要结果的时候,再来调用"返回函数"。
上面的代码输出如下:
export1 month: 10 doing...
export1 done!
----------------
<function export2.<locals>.do at 0x107a24a60>
export2 month: 10 doing...
export2 done!
闭包
很多语言都支持闭包特性,python中当然少不了这个,参考下面的示例:
def my_sqrt1(n):
r = [] def do():
for i in range(1, n + 1):
r.append(i ** 2)
return r return do a = my_sqrt1(4)
print(type(a))
b = a()
print(type(b))
print(b)
输出:
<class 'function'>
<class 'list'>
[1, 4, 9, 16]
闭包有一个经典的坑:不要在闭包函数中使用“值会发生变化的变量"(比如:for循环中的变量)。原因是:python中的闭包本质上是是"内部函数"延时计算,如果有循环变量,循环过程中闭包函数并不会执行,等循环结束了,闭包中引用的循环变量其实是循环结束后最终的值。说起来有点绕口,看下面的示例:
def my_sqrt2(n):
r = []
for i in range(1, n + 1):
def do():
r.append(i ** 2)
return r return do a = my_sqrt2(4)
print(type(a))
b = a()
print(type(b))
print(b)
输出:
<class 'function'>
<class 'list'>
[16]
解释一下:调用a = my_sqrt2(4)时,my_sqrt2(4)马上执行完了,这时候里面的fox循环执行完了,最后i的值停在4,然后这个值被封闭在do函数里,并没有马上执行。然后再调用a()时,这时候才真正调用do()函数,此时i值=4,所以最终r[]列表里,就只追回了一个值4*4=16
如果非要使用循环变量,只能想招儿把这个循环变量,也封闭到一个内部函数里,然后再使用,比如下面这样:
def my_sqrt3(n):
def f(j):
def g():
return j ** 2 return g r = []
for i in range(1, n + 1):
r.append(f(i)) return r a = my_sqrt3(4)
print(type(a)) for x in a:
print(x())
这个例子仔细研究下蛮有意思的,r.append(f(i)),列表里追加的并非计算结果,而是f(j)里返回的函数g,所以a = my_sqrt3(4)这里,a得到的是一个function组成的list,然后list里的每个g函数实例,都封闭了当次循环的变量i,因为闭包的缘故,i值已经被封印在g内部,不管外部的for循环如何变量,都不会影响函数g。
输出如下:
<class 'list'>
1
4
9
16
关于闭包,最后再来看一个廖老师教程上的作业题,用闭包的写法写一个计数器:
def create_counter1():
r = [0] def counter():
r[0] += 1
return r[0] return counter count = create_counter1(); print([count(), count(), count()])
输出:
[1, 2, 3]
对于有洁癖的程序员,可能会觉得要额外设置一个只保存1个元素的list,有点浪费。可以换种写法:
def create_counter2():
n = 0 def counter():
nonlocal n
n += 1
return n return counter count = create_counter2(); print([count(), count(), count()])
输出:
[1, 2, 3]
注意这里有一个关键字nonlocal,号称是python3新引入的关键字,为的是让闭包的内部函数里面,能读写内部函数外的变量。(但是在第1种写法中,r=[0]不也是定义在外部么?区别就是list是复杂的变量类型,而第2种写法中n是简单类型的变量,做为python初学者,不是很理解这个哲学思想^_~)
三、aop/装饰器
aop是java生态体系中的精髓之一,而python里同样能做到,而且看上去更简洁。
比如有一个加法函数:
def add1(i, j):
return i + j
想在add1调用时,自动把入参,返回结果,以及执行时间都记录下来,可以这么做,再定义一个log函数(类似java中的aspect切面定义)
import time def log(fn):
def do(*args, **kw):
start = time.time()
result = fn(*args, **kw)
end = time.time()
print("function=>", fn.__name__, ",args1=>", args, ",args2=>", kw, ",result=>", result, ",exec_time=>",
(end - start) * 1000, "ms")
return result return do
然后在add1函数上加上这个"注解"就可以了
@log
def add1(i, j):
return i + j print(add1(1, 2))
输出:
function=> add1 ,args1=> (1, 2) ,args2=> {} ,result=> 3 ,exec_time=> 0.0030994415283203125 ms
3
如果aop本身的"切面"也需要传参数进来,比如:在日志前想附加一段特定的前缀,可以参考下面这样:
import time def log(fn):
def do(*args, **kw):
start = time.time()
result = fn(*args, **kw)
end = time.time()
print("function=>", fn.__name__, ",args1=>", args, ",args2=>", kw, ",result=>", result, ",exec_time=>",
(end - start) * 1000, "ms")
return result return do def log2(log_prefix):
def around(fn):
def do(*args, **kw):
start = time.time()
result = fn(*args, **kw)
end = time.time()
print(log_prefix, ",function=>", fn.__name__, ",args1=>", args,
",args2=>", kw, ",result=>", result, ",exec_time=>",
(end - start) * 1000, "ms")
return result return do return around @log2("调用日志:")
@log
def add2(i, j):
time.sleep(1)
return i + j print(add2(1, 2))
输出:
function=> add2 ,args1=> (1, 2) ,args2=> {} ,result=> 3 ,exec_time=> 1000.8599758148193 ms
调用日志: ,function=> do ,args1=> (1, 2) ,args2=> {} ,result=> 3 ,exec_time=> 1001.0490417480469 ms
3
注:这里我们刻意把log,log2同时运用在add2上,从输出上看,二个aspect都起作用了。
四、偏函数
还是拿add(i,j) 这个来说事儿吧,如果我们经常会遇到一个场景:想让某个数字固定+10,在其它语言里,通常是再定义一个类似add_10(i)的重载版本(java/c#都是这么干的),但是对于python来说,可以更优雅:
import functools def add(i, j):
return i + j # 偏函数
add_10 = functools.partial(add, j=10) print(add(1, 2))
print(add_10(1))
输出:
3
11
这种把参数列表中的某些常用项固定,然后再生成一个别名函数的玩法,就称为偏函数
参考文档:
1、廖雪峰的python教程:函数式编程
python:函数的高级特性的更多相关文章
- Python:笔记(4)——高级特性
Python:笔记(4)——高级特性 切片 取一个list或tuple的部分元素是非常常见的操作.Python提供了切片操作符,来完成部分元素的选取 除了上例简单的下标范围取元素外,Python还支持 ...
- Python的一些高级特性
内容基本上来自于廖雪峰老师的blog相当于自己手打了一遍,加强加强理解吧. http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493 ...
- C++复习11.函数的高级特性
C++ 函数的高级特性 20131002 C++函数增加了重载(override), inline, const, virtual四种机制,重载和内联机制既可以用于全局函数,也可以用于类的成员函数.c ...
- Python面向对象编程高级特性
***这里还是根据网上资料,主要是廖雪峰老师的教程学习的笔记,主要介绍python面向对象的高级特性,笔记不全,只是记录自己觉得容易出错的地方*** 1.python作为一种动态语言,他的动态绑定机制 ...
- Python序列函数、高级特性及高阶函数
序列函数: enumerate: for循环时记录索引,逐个返回元组(i, item) sorted:返回新的有序列表 zip:压缩将多个序列的对应位置的元素组成元组 zip(*元组列表): 解压缩 ...
- Python的一些高级特性以及反序列化漏洞
0x01 简述 文章主要记录一下python高级特性以及安全相关的问题 python作为脚本语言,其作为高级语言是由c语言开发的,关于python的编译和链接可以看向这里https://github. ...
- 002-python函数、高级特性
1.函数 1.1 定义函数 在Python中,定义一个函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回 自定义一个求绝对 ...
- python 9学习 高级特性
高级特性 掌握了Python的数据类型. 语句 和函数,基本上就可以编写出很多有用的程序了. 比如构造一个1, 3, 5, 7, ..., 99的列表,可以通过循环实现: L = [] n ...
- Python中的高级特性
1.切片.使用“[”和“]”即可,类似Matlab,可以切list,tuple,字符串等. 2.迭代.Python内置的enumerate函数可以把一个list变成索引-元素对. 3.列表生成式.列表 ...
随机推荐
- [学习笔记]JS 数组Array push相关问题
前言: 今天用写了一个二维数组,都赋值为零,然后更新其中一个值,结果和预期是不一样,会整列的相同位置都是同一个值. 1.用Chrome的控制台样例如下: arrs[2][2] =1的赋值,竟然是三个数 ...
- android 手机拍照返回 Intent==null 以及intent.getData==null
手机拍照第一种情况:private void takePicture(){ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);Si ...
- visual studio 2017 installer 安装包制作过程出现的问题---无法注册模块 HRESULT -2147024769 请与您的技术支持人员联系
使用visual studio 2017 installer制作打包程序时如果用到了外部控件需要按以下方式操作: 1.将应用程序及应用程序所用到的所有DLL拷贝到打包目录,加入打包程序之中. 2.将应 ...
- fatal error: google/protobuf/arena.h:没有那个文件或目录
安装caffe时make all会出现这个错误,按照https://github.com/BVLC/caffe/issues/4988说法,可能时libprotobuf-dev过时了,需要从源码重新变 ...
- Jmeter接口测试实例图文示例
以getObjectByCode接口为例,用jmeter2.13来进行接口测试. 测试前准备: 测试工具及版本:jmeter 2.13 r1665067(须包含__MD5函数) 示例接口:8.1根据单 ...
- python使用ssdb的队列,用于替换canal+rabbitmq
# pip install -i https://mirrors.aliyun.com/pypi/simple/ pyssdb import pyssdb c = pyssdb.Client('172 ...
- IntelliJ IDEA快捷键:Ctrl+Shift+空格
The smart type code completion may be used after the new keyword,to instantiate an object of the exp ...
- SpringMVC的初始
1:其实一开始对SSH和SSM并不是很熟悉的,对SSH可能熟悉些(Struts,Spring,Hibernate)这三个框架.但是由于框架的更新,和出现了更好的框架,一些老框架就被淘汰了,但是呢,一些 ...
- 【BZOJ1135】[POI2009]Lyz
题解: hall定理..第一次听说 思考了半小时无果 二分图匹配时间显然太大 但是有这个hall定理 二分图有完美匹配的充要条件是 对于左边任意一个集合(大小为|s|),其连边点构成的集合(大小为|s ...
- 【BZOJ3307】雨天的尾巴
题解: win下的10mb和linux下的好像不是很一样 明天再看看 求lca用的离线求,注意bz数组开2*n 这道题的线段树合并还是很好想的 我们只要把操作差分一下就好了 时间复杂度nlogn的 写 ...