Python常用内置函数filter、map、reduce、apply、zip

Python将复杂的数据结构隐藏在内置函数中,用C语言来实现,所以只要写出自己的业务逻辑,Python会自动得出你想要的结果。

内置函数主要有:filter、map、reduce、apply,结合匿名函数lambda、列表解析一起使用,功能更加强大。

如果对于大数据Hadoop和Spark感兴趣的话,最好学会这些内置函数的用法。因为Hadoop的分布式计算框架采用的是MapReduce模型,该模型的核心就是Map函数和Reduce函数。
Spark的核心建立在RDD上,RDD的转换API和动作API也包括filter、map和reduce,并且常与lambda结合使用。

使用内置函数的好处是:

  1. 使用内置函数,比普通的Python实现,速度要快一倍左右。
  2. 代码简洁。

1 lambda表达式

lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑。lambda表达式是起到一个函数速写的作用,允许在代码内嵌入一个函数的定义。

如果你不想在程序中对一个函数使用两次,你也许会想用lambda表达式,它们和普通的函数完全一样。

原型:

lambda  参数 : 操作(参数)

参数就是函数中所谓的入参,操作就是返回的计算结果,相当于return语句。

在数据分析的过程中,常常需要使用apply和lambda函数。比如:根据逾期天数来给数据打标签,如果逾期天数大于30天,则为逾期。

data['target'] = data['逾期天数'].apply(lambda x: 1 if x>30 else 0)

例1. 定义一个lambda表达式,求三个数的和。

>>> f=lambda x,y,z:x+y+x
>>> f(1,2,3)
4
>>> f=lambda x,y,z:x+y+z
>>> f(1,2,3)
6

例2. 用lambda表达式求n的阶乘。

>>> n=5
>>> reduce(lambda x,y:x*y,range(1,n+1))
120

例3. lambda表达式也可以用在def函数中。

>>> def action(x):
        return lambda y:x+y
>>> a=action(2)
>>> a(22)
24

这里定义了一个action函数,返回了一个lambda表达式。其中lambda表达式获取到了上层def作用域的变量名x的值。

a是action函数的返回值,a(22),即是调用了action返回的lambda表达式。这里也可以把def直接写成lambda形式。如下:

>>> b=lambda x:lambda y:x+y
>>> a=b(3)
>>> a(2)
5
>>>(b(2))(2)
4

2 filter

>>> help(filter)
Help on built-in function filter in module __builtin__:

filter(...) 
    filter(function or None, sequence) -> list, tuple, or string

Return those items of sequence for which function(item) is true. 
If function is None, return the items that are true. If sequence is a tuple or string,
return the same type, else return a list.

用途:

用于过滤与函数func()不匹配的值, 类似于SQL中select value != 'a'。相当于一个迭代器,调用一个布尔函数func来迭代seq中的每个元素,返回一个是bool_seq返回为True的序列。

filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

第一个参数:函数或None。
第二个参数:序列。

说明:

  • 如果第一个参数为function,那么返回条件为真的序列(列表,元组或字符串)。
  • 如果第一个参数为None的话,那么返回序列中所有为True的项目。

顾名思义,filter过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列表,符合要求即函数映射到该元素时返回值为True。这里是一个简短的例子:

number_list = range(-5, 5)
less_than_zero = filter(lambda x: x < 0, number_list)
print(list(less_than_zero))

# 输出为: 
[-5, -4, -3, -2, -1]

这个filter类似于一个for循环,但它是一个内置函数,并且更快。大部分情况下推导式的可读性更好。

例1. 要过滤掉所有值为False的列表。

print filter(None,[-2,0,2,'',{},()])    # 输出[-2,2],其余的都为False

例2. 过滤某个字母。

>>> filter(lambda x: x !='a','abcd')
'bcd'

例3. 过滤字母以B开头的人名。

>>> names = ['Alice','Bob','Smith','David','Barbana']
>>> filter(lambda x: x.startswith('B'),names)
['Bob', 'Barbana']

例4. 过滤fib列表中的奇数,偶数项。

>>> fib = [0,1,1,2,3,5,8,13,21]
>>> filter(lambda x: x%2,fib)   # 实际上等同于x%2==1的项才过滤
[1, 1, 3, 5, 13, 21]
>>> filter(lambda x: x%2==0,fib)
[0, 2, 8]

例5. 将2-20间所有质数列出来。

>>> filter(lambda x: not [x for i in range(2,x) if x%i==0],range(2,20))
[2, 3, 5, 7, 11, 13, 17, 19]

例6. 过滤人名为空的序列。

>>> names = ['Alice','Jerry','Sherry','Bob','Tom','']
>>> filter(None,names)
['Alice', 'Jerry', 'Sherry', 'Bob', 'Tom']

例7. 过滤某目录下所有以test.py结尾的文件。

import os,re                                   # 需要的模块 
files = os.listdir(r'D:\python')               # 列出需要查找的目录的所有文件 
test = re.compile('test.py$',re.IGNORECASE)    # re.IGNORECASE忽略大小写 
print filter(test.search,files)                # 过滤所有满足条件的文件 
>>> 
['1test.py', 'test.py']

例8. 过滤所有子列表中,单词为'Python'的。

def filter_word(word): 
    try: 
        return word != 'Python' 
    except ValueError: 
        return False 

words = [['Perl','Python','Shell'],['Java','C/C++'],['VB','Dephi']] 
print [filter(filter_word,word) for word in words]   # 用了列表解析的方法

filter的逻辑实现:

def filter(func,seq):
    f_seq = []                    # 建一个空序列,用于存储过滤后的元素 
    for item in seq:              # 对序列中的每个元素进行迭代 
        if func(item):            # 如果为真的话 
            f_seq.append(item)    # 满足条件者,则加入 
    return f_seq                  # 返回过滤后的元素

>>> print filter(lambda x: x>0,[-2,0,2])   # 对匿名函数进行过滤,返回正值[2]

3 map

>>> help(map)
Help on built-in function map in module __builtin__:

map(...) 
    map(function, sequence[, sequence, ...]) -> list

Return a list of the results of applying the function to the items ofthe argument sequence(s). 
If more than one sequence is given, the function is called with an argument list consisting
of the corresponding item of each sequence, substituting None for missing values when not all
sequences have the same length. If the function is None, return a list of the items of the
 sequence (or a list of tuples if more than one sequence).

用途:

对一个及多个序列执行同一个操作,返回一个列表。

说明:

  • 返回一个列表,该列表是参数func对seq1,seq2处理的结果集。
  • 可以有多个序列,如果函数为None的话,返回一个序列的列表。

map规范:

map(function_to_apply, list_of_inputs)

大多数时候,我们要把列表中所有元素一个个地传递给一个函数,并收集输出。比方说:

items = [1, 2, 3, 4, 5]
squared = []
for i in items:
    squared.append(i**2)

Map可以让我们用一种简单而漂亮得多的方式来实现。就是这样:

items = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, items))

大多数时候,我们使用匿名函数(lambda)来配合map, 所以我在上面也是这么做的。 不仅用于一列表的输入, 我们甚至可以用于一列表的函数!

def multiply(x):
    return (x*x)
def add(x):
    return (x+x)

funcs = [multiply, add]
for i in range(5):
    value = map(lambda x: x(i), funcs)
    print(list(value))

# 输出为:
[0, 0]
[1, 2]
[4, 4]
[9, 6]
[16, 8]

例1. 常规用法。

>>> map(lambda x: x+1,[1,2,3,4])
[2, 3, 4, 5]

>>> map(lambda x,y: x+y,[1,2,3,4],(10,20,30,40)) 
[11, 22, 33, 44]

#第一个序列中的第五个元素存在,但在第二个序列中不存在,所以y为False,所以执行5+10 
>>> map(lambda x,y: x+y if y else x+10,[1,2,3,4,5],(1,2,3,4)) 
[2, 4, 6, 8, 15]

>>> map(None,[1,2,3,4,5],(1,2)) #如果是None的话,以None来补齐短序列造成的空缺 
[(1, 1), (2, 2), (3, None), (4, None), (5, None)]

>>> names = ['Alice','Jerry','Bob','Barbar'] 
>>> map(len,names) #求列表中每个元素的长度 
[5, 5, 3, 6]

>>> m = [1,4,7] 
>>> n = [2,5,8] 
>>> map(None,m,n)
[(1, 2), (4, 5), (7, 8)]

>>> import operator #比较两个列表中元素大小 
>>> a = [1,2,3]
>>> b = [0,4,9] 
>>> map(operator.gt,a,b) 
[True, False, False]

例2. 求0-5之间数,[本身,平方,立方],如:元素2,则返回:[2,4,8]。

def func1(x): 
   return x           # 返回自身 
def func2(x): 
   return x ** 2      # 返回平方 
def func3(x): 
   return x ** 3      # 返回立方

funcs = [func1,func2,func3]   # 函数列表 

for i in range(5):        # 遍历列表
   print map(lambda func: func(i),funcs)  # 对其中每个元素执行func1(i),func2(i),func3(i)操作

例3. 实现下面的逻辑结构。

1.0 [1,2,3,4,5]
2.0 [1,2,3,4,5]
....

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3,4,5]

def test(foo): 
    print foo,bars

print map(test,foos)

例4. 结合map和filter,求0-10之间偶数的平方,汇合成列表。

>>> map(lambda x:x**2, filter(lambda x: x%2==0,range(10))) 
[0, 4, 16, 36, 64]

map的逻辑实现:

def map(func,seq): 
    map_seq = []                          # 建空序列 
    for item in seq:                      # 对序列中每个元素进行处理 
        map_seq.append(func(item))        # 往空序列中添加func处理过的元素 
    return map_seq                        # 返回最后的列表

print map(lambda x: x * 2,[1,2,3,4])      # [2,4,6,8]

4 reduce

>>> help(reduce)
Help on built-in function reduce in module __builtin__:

reduce(...) 
    reduce(function, sequence[, initial]) -> value

Apply a function of two arguments cumulatively to the items of a sequence, from left to right, 
so as to reduce the sequence to a single value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) 
calculates ((((1+2)+3)+4)+5). If initial is present, it is placed before the items of the sequence
in the calculation, and serves as a default when the sequence is empty.

用途:

func为二元函数,将func作用于seq序列的元素,每次携带一对(先前的结果以及下一个序列的元素),连续的将现有的结果和下一个值作用在获得的随后结果上,最后减少我们的序列为一个单一的返回值:如果初始值init给定,第一个比较会是init和第一个序列元素而不是序列的头两个元素。

说明:

在Python3.0里面必须导入functools模块,from functools import reduce。reduce返回的必然是一个值,可以有初始值。

当需要对一个列表进行一些计算并返回结果时,reduce是个非常有用的函数。

举个例子,当你需要计算一个整数列表的乘积时,通常在python中你可能会使用基本的for循环来完成这个任务。
现在我们来试试reduce:

from functools import reduce
product = reduce((lambda x, y: x * y), [1, 2, 3, 4])
# 输出为:
24

例子:

 >>> reduce(lambda x,y: x+y, [47,11,42,13])
 113

其实现过程如下图所示:

其实现过程等同下面的:

>>> import operator 
>>> reduce(operator.add,[47,11,42,13]) 
113

注意:

  1. func()函数不能为None,否则报错。
>>> reduce(None,[1,2,3,4])
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module>
TypeError: 'NoneType' object is not callable
  1. func(x,y)只能有两个参数,否则报错。
>>> reduce(lambda x,y,z: x+y+z, [1,2,3,4],9)
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module>
TypeError: <lambda>() takes exactly 3 arguments (2 given)

reduce的逻辑实现:

def reduce(func,seq,init=None):
    l_seq = list(seq)    
    if init is None:  
        res = l_seq.pop(0) 
    else: 
        res = init 
    for item in l_seq: 
        res = func(res,item)     # func(res,item)作为结果集传给res 
    return res                   # 返回结果集

print reduce(lambda x,y:x+y,[1,2,3,4])      # 结果为10
print reduce(lambda x,y:x+y,[1,2,3,4],10)   # 结果为20,init初始值为10

5 apply

>>> help(apply)
Help on built-in function apply in module __builtin__:

apply(...) 
    apply(object[, args[, kwargs]]) -> value

Call a callable object with positional arguments taken from the tuple args, and keyword arguments 
taken from the optional dictionary kwargs. Note that classes are callable, as are instances with
a __call__() method.Deprecated since release 2.3. Instead, use the extended call syntax:
function(*args, **keywords).

用途:

当一个函数的参数存在于一个元组或者一个字典中时,用来间接的调用这个函数,元组或者字典中的参数按照顺序传递。

说明:

  • args是一个包含按照函数所需参数传递的位置参数的一个元组,假如func(a=1,b=2),那么这个元组中就必须严格按照这个参数的位置顺序进行传递(a=3,b=4),而不能是(b=4,a=3)这样的顺序。
  • kwargs是一个包含关键字参数的字典,而其中args如果不传递,kwargs需要传递,则必须在args的位置留空。

apply函数的返回值就是func函数的返回值。其实apply(func,args,kwargs)从Python2.3开始,已经被function(*args,**kwargs)代替了。

例1. 常规使用

def func1():               # 无参函数 
    print 'No Args!'

def func2(arg1,arg2):      # 两个参数 
    print arg1,arg2

def func3(arg1=1,arg2=2):  # 带字典函数
    print arg1,arg2

if __name__=='__main__': 
    apply(func1) 
    apply(func2,('Hello','World!')) 
    apply(func3,(),{'arg1':'This is param1','arg2':'This is param2'})     # 注意元组参数为()

既然可以用function(args,*kwargs)来代替apply(),那么apply有什么好处呢,几个看得见的好处:

  1. 如果函数名,变量名太长的话,用apply()还是很方便的。
  2. 如果不能确认有多少变量在args里面时,则必须使用apply,它能动态加载变量及函数。
# Sorry about the long variable names
args = function_returning_list_of_numbers()
func = function_returning_a_function_which_operates_on_a_list_of_numbers()

# You want to do f(arg[0], arg[1], ...) but you don't know how many arguments are in 'args'. For this you have to use 'apply': result = apply(func, args)

3.如果函数名作为一个对象来传递时,用apply()很方便。

def test(f,a,b): 
    print 'test' 
    print f(a,b)

test(func,a,b)

可以看出,test函数的第一个参数f就是一个函数对象。我们将func传递给f,那么test中的f()所做的实际上就是func()所实现的功能。这样,就大大提供了程序的灵活性。假如我们有另外一个函数取代func,就可以使用相同的test函数。

test(lambda x,y: x ** 2 + y,2,3)
>>> test
7

6 zip

>>> help(zip)
Help on built-in function zip in module __builtin__:

zip(...) 
    zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]

Return a list of tuples, where each tuple contains the i-th element from each of the argument
sequences. The returned list is truncated in length to the length of the shortest argument sequence.

用途:

返回一个元组列表,该元组按顺序包含每个序列的相应元素,以最小的一个为准。

说明:

zip()是Python的一个内建函数,它接受一系列可迭代的对象作为参数,将对象中对应的元素打包成一个个tuple(元组),然后返回由这些tuples组成的list(列表)。若传入参数的长度不等,则返回list的长度等于参数中长度最短的对象的长度。

例子:

>>> zip(range(5),range(1,20,2))
[(0, 1), (1, 3), (2, 5), (3, 7), (4, 9)]

>>> x=(1,2,3); y=(4,5);z=(6,)
>>> zip(x,y,z)
[(1, 4, 6)]

zip的逻辑实现:

def zip(*iterables): 
    sentinel = object() 
    iterators = [iter(it) for it in iterables] 
    while iterators:
        result = [] 
        for it in iterators: 
             elem = next(it, sentinel)
             if elem is sentinel: 
                  return result.append(elem) 
        yield tuple(result)

如果您发现文中有不清楚或者有问题的地方,请在下方评论区留言,我会根据您的评论,更新文中相关内容,谢谢!

推荐阅读更多精彩内容