Python学习笔记(11)-函数式编程

1.高阶函数

定义函数时,将函数作为参数,就可叫做高阶函数,使用高阶函数编程就是函数式编程。

1.1变量可以指向函数,函数名也是变量

在python中,函数名也是变量,在之前的笔记中,可以给函数更换名字,例如。

代码:

my = abs  # 函数名abs指向abs()函数,变量my也指向abs()函数
print(abs(-4))  
print(my(-4)) 

结果:

4
4

如果函数名(即变量)abs指向了其他的数据,或者函数名abs的指向被删除了,则原来的abs()函数不可再用函数名abs来调用,例如

代码:

abs = 1  # 变量abs已经指向整数1,不再指向abs()函数
# del abs    此句是删除abs的指向
print(abs(-4))  # 变量abs已经不再指向abs()函数了

结果:

TypeError: 'int' object is not callable
#  NameError: name 'abs' is not defined

所以在定义函数时,变量可以作为参数,同样,函数名也可作为参数,将函数传递到另一个函数中。例如

代码:

# 定义平方的函数
def square(number):
    return number ** 2


# 按照传递进去的函数逻辑,计算两个数的结果和
def test(x, y, method):
    print(method(x) + method(y))


test(1, 3, square)  # 传进去求平方的逻辑
test(1, -3, abs)    # 传进去求绝对值的逻辑,注意是abs()函数的函数名,即变量abs

结果:

10
4

1.2map()redued()函数的使用

map()函数接收两个参数,第一个是函数,第二个是一个Iterable(可迭代的对象),每个元素都将运行该函数,并将结果作为一个Iterator返回(注意:Iterator不能直接打印输出存储结果)

例如,求列表里每个数的3次方(其实循环也可以实现,但是用map()函数更直观方便,更容易理解)

代码:

def cube(x):  #  定义计算3次幂的函数
    return x ** 3


nums = [1, 2, 3, 4, 5]
result = list(map(cube, nums))  # 使用map函数,将计算函数和数据传入,再通过list将对象变成列表对象
print(result)

结果:

[1, 8, 27, 64, 125]

reduce()函数也接收两个参数,第一个是函数并且该函数必须有2两个参数,第二个参数是传进去的数据。reduce()会按照传进的函数从数据中取2个元素,并将结果和下一个元素再次当做运算的2个元素,以此运算。使用reduce()函数,需要导入,例如from functools import reduce

代码:

from functools import reduce

# 此函数没啥意义,纯粹是为了练习python语法而已→_→
def prod(L):
    def mul(x, y):  #  函数里定义了相乘的函数
        return x * y

    return reduce(mul, L)  # 从L中取前两个运算,然后将结果和第三个再作为两个参数进行运算,以此类推


print('1*2*3*4 =', prod([1, 2, 3, 4]))

结果:

1*2*3*4 = 24

map()reduce()结合的小栗子,同上,没啥意义,纯粹为了练习(⊙o⊙)…

将字符串转换成浮点数 strnum = '123.456',其实完全可以用float(strnum)

代码:

from functools import reduce  # 导入reduce

strnum = '9876.1234'

def str2float(str=''):
    nums = str.split('.')  # split函数将按照传入的参数分隔字符串并封装到列表中返回
    str1 = nums[0]         # 小数点前的字符串
    str2 = nums[1]         # 小数点后的字符串

    def char2num(s):       # 构造字符转换成数字函数
        return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]

    def str2num(x, y):     # 转换成数字后再运算得出整数
        return x * 10 + y

    a = reduce(str2num, map(char2num, str1))
    b = reduce(str2num, map(char2num, str2))
    result = a + b / 10 ** (len(str2))  # 注意是10的n次幂运算
    return result

print(str2float(strnum))

结果:

9876.1234

1.3filter()函数

filter()函数大致同map()函数,也是接收一个函数和一个列表,不同的是,filter()会将每次运行完传入的函数方法后判断,根据TrueFalse来决定是否要保存这个元素,最后返回的也是一个Iterator对象,需要用list()才能显示出来。例如

代码:

def saveS(s):  # 传入的字符创中如果有s返回True,否则返回False
    return 's' in s


L = ['abc', 'dsef', 'asdf', 'ee', 'sht']

print(list(filter(saveS, L)))  # 返回True时保留

结果:

['dsef', 'asdf', 'sht']

e.g. 用filter()求素数,使用方法逻辑是埃氏筛法

代码:

# 初始化一个序列,从3开始的奇数(偶数不是素数)
def initNumber():
    n = 1
    while True:
        n += 2
        yield n


# 定义一个筛选函数,能被n整除的返回False,不能整除的返回True
def check(n):
    return lambda x: x % n > 0


def getPrime(maxNum):
    yield 2  # 生成器返回第一个素数
    init = initNumber()  # 调用初始化函数 3,5,7,9,11,13,15,17,19,21...
    while True:
        n = next(init)  # 取得初始化的第一个数3
        if n < maxNum:
            yield n  # 生成器返回第一个数3(第一个数总是素数)
            init = filter(check(n), init)  # 过滤3的倍数5,7,11,13,17,19... 如此循环
        else:
            break


print(list(getPrime(100)))

结果:

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

1.4sorted()函数

笔记(2)中,已经记录了对列表的排序,里面的sorted()可以只传入列表进行临时排序,也可再添加按照相应的逻辑来排序(即传入函数名,使用命名关键字key=xxx) 例如

代码:

L = ['a', 'c', 'M', 'F', 'e']
print(sorted(L))  # 原列表排序
print(sorted(L, reverse=True))  # 列表反序
print(sorted(L, key=str.lower))  # 按照元素的字母的ASCII码(无视大小写,函数名也可换做str.upper)

结果:

['F', 'M', 'a', 'c', 'e']
['e', 'c', 'a', 'M', 'F']
['a', 'c', 'e', 'F', 'M']

2.返回函数和闭包

函数不仅可以作为参数传递,还可作为返回值来返回,例如

代码:

def getInfo(name):  # 函数A
    def getAge(age):  # 函数A中定义了函数B
        return 'name=' + name + ',age=' + str(age)

    return getAge  # 返回函数,即指向函数B的变量(函数名)


test1 = getInfo('zhangsan')  # 调用getInfo函数,返回一个函数,即之前定义好的getAge()函数,用test1指向它
test2 = getInfo('lisi')
print (test1(22))  # 再调用返回的函数
print (test2(22))

结果:

name=zhangsan,age=22
name=lisi,age=22

什么是闭包呢?简单的讲就是调用函数A,而A返回了一个函数B,并且B引用了传递给A的参数(或者是A内部定义的局部变量),这个函数B就叫做闭包,这个参数就是闭包的自由变量

如上述的代码中,调用getInfo()时传递的参数(即自由变量)是不同的,即使闭包getAge()调用的时候test1(22)传入相同的参数,结果仍是不同的。

需要注意的是:返回的闭包函数没有立即执行,而是在调用(例如test1(22))的时候才会执行,举个栗子

代码:

def count():  # 函数A
    fs = []  # 创建一个空列表
    for i in range(1, 4):  # 循环3次,i是A的变量
        def f():  # 函数A中定义的函数B,引用了A的变量i
            return i * i  # 返回表达式(也是个函数),并没有直接调用

        fs.append(f)  # 添加到列表中,循环完毕后i的值已经是3
    return fs


f1, f2, f3 = count()  # f1,f2,f3分别指向列表中的3个函数
print (f1())  # 分别调用函数,注意,此时调用的时候直接从第五行代码开始执行,此时的i已经是3
print (f2())
print (f3())

结果:(结果不是1,4,9,可在debug模式下看执行步骤)

9
9
9

由此可见,在闭包函中的自由变量,或者是调用的A的局部变量尽量不要使用动态变化的。

3.匿名函数

先看代码,求一个数的3次方,可用如下代码

代码:

L = [1, 2, 3, 4, 5]


def cube(x):
    return x ** 3


print (list(map(cube, L)))

结果:

[1, 8, 27, 64, 125]

如果某些函数只调用一次,可以用lambda表达式来构造匿名函数,不用再显示的定义函数,如下

代码:

L = [1, 2, 3, 4, 5]

print (list(map(lambda x: x ** 3, L))) 

结果:

[1, 8, 27, 64, 125]

lambda x: x ** 3实际上就是上边定义的函数cube,第一个x是参数(可不写,即为无参函数),后边的是函数逻辑,整个表达式返回一个函数变量,匿名函数也可作为返回值。

4.装饰器

在定义好的函数运行期间,不修改此函数,动态地给该函数增添功能,即为装饰器Decorator,说白了就是传递函数和返回函数的运用,下边是个最简单的例子,例如。详细请看

代码:

def deco(fun):
    print('---运行之前---')
    fun()
    print('---运行之后---')


def myfun():  
    print('运行了函数')


deco(myfun)  #  将函数作为参数传递

结果:

---运行之前---
运行了函数
---运行之后---

5.偏函数

如果调用某个函数的时候,该函数的参数过多,需要简化,可以使用functools模块的partial,例如

int()可以将字符串转换成整数,只传入字符串时,默认按照十进制转化,若添加base参数,则按照所传的进制计算

代码:

print(int('1234567'))
print(int('1234567', base=8))
print(int('1234567', base=16))

结果:

1234567
342391
19088743

除了每次都传入base参数,还可用partial,例如

代码:(其实就是外部更改了函数参数默认值)

import functools

int2 = functools.partial(int, base=2)  # int函数外固定其参数base默认值是2
print(int2('101010001001'))  #  每次调用无需再传入参数base
print(int2('101101'))
print(int2('101101', base=8)) # 当手动传入base参数的时候,按照传入的数据计算

结果:

2697
45
33345
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值