3.函数式编程

高阶函数
map
reduce
filter
sort
返回一个函数
闭包
匿名函数
装饰器
偏函数

1.特点
函数式编程允许把函数本身传入另一个函数,并且允许返回另一个函数。

2.高阶函数
高阶函数可以接收一个函数作为参数;

f = abs
a = 1
b = -2


# 将函数作为参数传入到另一个函数
def submit(x, y, z):
    return z(x) + z(y)


print(submit(a, b, f))

输出结果:
3

3.map函数

map函数接收两个参数,一个是函数,另一个是可变参数形式的可迭代对象Iterable,有几个参数,map的第二个参数就传几个Iterable,map会将传的函数依次作用到Iterable的每个值,并把结果作为新的Iterator返回。

def f1(x):
    return x * x

# r是一个Iterator
r = map(f1, [1, 2, 3])
print(list(r))

输出结果:
[1, 4, 9]

def f2(x, y):
    return x * y


# r是一个Iterator
r = map(f2, [1, 2, 3], [1, 2, 4])
print(list(r))

输出结果:
[1, 4, 12]

4.reduce函数
reduce函数接收两个参数,一个是必须有两个参数的函数,另一个是一个可迭代对象Iterable,reduce会将传入的函数作用于Iterable的每一个值,将前两个元素的结果作为第一个参数继续与下一个参数作运算;

def f3(x, y):
    return x + y


r1 = reduce(f3, [1, 2, 3, 4, 5])
print(r1)

输出结果:
15

计算方式为f(f(f(f(1,2),3),4),5)

5.将一个string类型的数字转为int类型

# str转Int
def str2int(s):
    def f4(x, y):
        return x * 10 + y

    def f5(s1):
        return {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9}[s1]

    return reduce(f4, map(f5, s))

print(str2int("123456"))

输出结果:
123456

6.filter函数

filter函数接收两个参数,一个是函数,另一个是一个序列,filter会将传入的函数作用于Iterable的每一个值,如果结果返回true,则保留,否则丢弃;

# 筛选出一个序列中所有能整除2的数
def f1(i):
    return i % 2 == 0


l = filter(f1, [1, 2, 3, 4, 5])
print(list(l))

输出结果:
[2, 4]

7.Sort函数

# 普通大小排序
l = [2, 5, 1, -3, 6]
print(sorted(l))

# 转换成正数后再排序
print(sorted(l, key=abs))

# 英文排序,按首字母的ascll码的大小排序
l1 = ["Ckl", "Gak", "adfj", "bafk"]
print(sorted(l1))

# 转换成小写后再排序
print(sorted(l1, key=str.lower))

print(sorted(l1, key=str.lower, reverse=True))

输出结果:
[-3, 1, 2, 5, 6]
[1, 2, -3, 5, 6]
['Ckl', 'Gak', 'adfj', 'bafk']
['adfj', 'bafk', 'Ckl', 'Gak']
['Gak', 'Ckl', 'bafk', 'adfj']

8.返回一个函数
例如有一个求和的函数

def f1(index):
    count = 0
    for i in range(index):
        count = count + i
    return count


print(f1(5))
输出结果:
10

但是有时并不需要立即求和,这时就可以返回这个求和函数

def f2(index):
    def f3():
        count = 0
        for i in range(index):
            count = count + i
        return count

    return f3

f = f2(5)
print(f2(5))
print(f)
print(f())
输出结果:
<function f2.<locals>.f3 at 0x0000000001162510>
<function f2.<locals>.f3 at 0x0000000001162488>
10

注意:运行f()才会进行计算,11行的f和12行的f2(5)的不是同一个对象,所以结果也互相不影响,内部函数f3中引用了外部函数的变量index,当调用f2返回f3时,相关参数和变量会被保存在返回的函数中,这种形式被称为闭包。

9.闭包注意事项

def f1():
count = []
for i in range(3):
    def f2():
        return i * i

    count.append(f2)
return count


m = f1()
print(m[0](), m[1](), m[2]())

例如上面的程序,常规会以为结果为0 1 4,但实际上结果为4 4 4,这是因为返回的函数并未立即执行,而是等到调用了f1()时才去执行,而这时的i已经变成了2了,所以三次的结果都为4,所以返回函数中不要引用后续会发生变化的变量;
如果一定要引用循环变量,可以再创建一个函数,用该函数的参数来绑定循环变量的值,这样就算该变量后续改变了,已经绑定到函数的参数还是不会变的;

def f3():
count = []

def f4(j):
    def f5():
        return j * j

    return f5

for i in range(3):
    # 带括号的方法会立即执行,所以用的是i的当前值
    count.append(f4(i))
return count


m1 = f3()
print(m1[0](), m1[1](), m1[2]())
输出结果:
0 1 4

10.匿名函数
在python中,用关键字lambda表示匿名函数
常规函数:

def f1(x):
return x + x


print(list(map(f1, [1, 2, 3])))
输出结果:
[2, 4, 6]

使用匿名函数:

print(list(map(lambda x: x + x, [1, 2, 3])))
输出结果:
[2, 4, 6]

11.装饰器

例如已有一个函数f1,此时想在执行f1函数之前和之后做一些其它的事情,但是又不允许修改f1的代码,此时就可以用装饰器,装饰器可以增强函数的功能;

def d(func):
    def wrapper(*args, **kwargs):  # 一个可变参数加一个关键字参数可接收任意参数的调用
        print('before call %s():' % func.__name__)  # .__name__为获取该函数的name的方法
        func1 = func(*args, **kwargs)
        print('after call %s():' % func.__name__)
        return func1

    return wrapper


@d
def f1():
    print("f1 print")


f1()

输出结果:
before call f1():
f1 print
after call f1():

通过在f1函数前@d的方式调用f1()时,相当于调用了d(f1),d()返回了一个函数wrapper(),wrpper函数里会运行作为参数传入d方法的f1()函数,并且在运行这个原始函数前后可以做一些需要增强的功能,例如这里在运行f1()函数前后输出一条日志;

如果想传一些参数到装饰器里,可以这样写:

def d1(msg):
def d(func):
    def wrapper(*args, **kwargs):
        print('before call %s():' % func.__name__, msg)
        func1 = func(*args, **kwargs)
        print('after call %s():' % func.__name__, msg)
        return func1

    return wrapper

return d


@d1('ssssss')
def f2():
    print("f2 print")


f2()
输出结果:
before call f2(): ssssss
f2 print
after call f2(): ssssss

此时如果调用下面语句会发现一个问题:

print(f2.__name__)
输出结果:
wrapper

明明是f2,却变成了装饰器里的wrapper;
原因分析:
前面d的方式调用f1()时实际是这样的

f1 = d(f1)

然后d()函数又返回了一个wrapper函数;
后面d1的方式调用f2()时实际是这样的

f2 = d1("ssssss")(f2)

然后d1()函数返回了一个参数为f2的d()函数,d()函数又返回了wrapper函数,所以两种方式最终都返回了wrapper函数,经过装饰后的函数的name已经从原来的f1和f2变成了wrapper了。
想让原始函数的名字不变,就得把原始函数的name等属性复制到wrapper函数中,Python内置函数functools.wraps可以做这个事;

def d1(msg):
def d(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print('before call %s():' % func.__name__, msg)
        func1 = func(*args, **kwargs)
        print('after call %s():' % func.__name__, msg)
        return func1

    return wrapper

return d

这时再运行

print(f2.__name__)

就会得到f2了。

12.偏函数
在基础1里了解到了五种参数类型,其中有一种是默认参数,可以在方法定义参数时直接定义好默认参数,如果调用方法时不传值,会默认取定义的默认值;

def f1(x, y=2):
print(x, y)

f1(1)
f1(1, 3)
输出结果:
1 2
1 3

例如此时有一个需求是调用多次f1方法,而且要传一个与定义的默认值不同的值,这时如果写多次f1(1,y=6)肯定是不爽的,不如直接定义一个新的默认值为6的方法,这时再写一个和f1逻辑完全一样的方法也是不合适的,因为就默认值的定义变了下,此时可以用Python内置偏函数functools.partial()来直接创建一个新的函数f2:

f2 = functools.partial(f1, y=6)

f2(1)
f2(6)
输出结果:
1 6
6 6

再例如Python内置的int()函数,可以把字符串转换成整数型

# 默认十进制
print(int('123'))
# 按16进制输出
print(int('123', base=16))
输出结果:
123
291

此时如果要进行多次按16进制运算某些值,就得写多次base=16,而且int()方法是内置的也无法修改,这时用functools.partial()来修改这个默认值就再适合不过了

int16 = functools.partial(int, base=16)
print(int16('123'))
输出结果:
291

猜你喜欢

转载自blog.csdn.net/Aislli/article/details/81163446