Python | 函数式编程

1 函数式编程

函数式编程(functional programming)其实是个很古老的概念,诞生距今快60年啦!
最古老的函数式编程语言Lisp
新出现的函数式编程语言:比如Erlang、Scala、clojure等
热门语言:Python、java、JavaScript、C++等都增加了函数式编程的一些特性。

函数式编程在某些时刻,非常方便! 但不需大家二选一
我们通过一些常见的函数式编程的内容,先学习,后体会“函数式编程”。

函数是一等公民!

  • 可以赋值
  • 可以作为参数传递
  • 可以作为返回值
  1. 函数式编程最鲜明的特点就是:函数是一等公民(first class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数或者作为别的函数的返回值。
  2. 一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数。
  3. Python内建的高阶函数有 map 、reduce、filter 、sorted
def test01():
    print("test01 function run!!!")


def test03(a, b):
    print(f"test03, {a}, {b}")


# *args, **kwargs 可变参数
def test2(func, *args, **kwargs):
    print("test02 function run...")
    func(*args, **kwargs)


a = test01  # test的地址给了a
print(a)
test2(a)
test2(test03, 100, 200)

在这里插入图片描述

2 lamda表达式(匿名函数)

lambda表达式可以用来声明匿名函数。lambda函数是一种简单的、在同一行中定义函数的方法。lambda 函数实际生成了一个函数对象。

lambda表达式只允许包含一个表达式,不能包含复杂语句,该表达式的计算结果就是函数的返回值。

lambda表达式的基本语法如下:

lambda arg1, arg2, arg3... : <表达式>

arg1、arg2、arg3 为函数的参数。<表达式>相当于函数体。运算结果是:表达式的运算结果。

f = lambda a, b, c: a + b + c
print(f)
print(f(2, 3, 4))

g = [lambda a: a * 2, lambda b: b * 3, lambda c: c * 4]
print(g[0](6), g[1](7), g[2](8))

在这里插入图片描述

3 偏函数

Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partialfunction)。要注意,这里的偏函数和数学意义上的偏函数不一样。

偏函数是用于对函数固定属性的函数,作用就是把一个函数某些参数固定住(也就是设置默认值),返回一个新的函数,调用这个新的函数会更简单。举例如下:

def int2(x, base=2):
    return int(x, base)


print(int2('1000000'))  # 64
print(int2('1010101'))  # 85

int0函数可以把字符串转换为整数,当仅传入字符串时int0函数默认按十进制转换,代码如下:

functools.partial就是帮助我们创建一个偏函数的,不需要我们自己定义 int2() ,可以直接使用下面的代码创建一个新的函数 int2:

import functools

int2 = functools.partial(int, base=2)  #
print(int2('1000000'))  # 64
print(int2('1010101'))  # 85
print(int2('1010010', base=10))  # 也可以修改base的值

在这里插入图片描述

4 闭包和自由变量

根据字面意思,可以形象地把闭包理解为一个封闭的包裹,这个包裹就是一个函数。当然还有函数内部对应的逻辑,包裹里面的东西就是自由变量(外部函数的局部变量),自由变量可以随着包裹到处游荡。
在这里插入图片描述

  • 局部变量:如果名称绑定再一个代码块中,则为该代码块的局部变量,除非声明为nonlocal或global
  • 全局变量:如果模块绑定在模块层级,则为全局变量
  • 自由变量:如果变量在一个代码块中被使用但不是在其中定义,则为自由变量(用完还存在)

我们知道,函数作用域是独立的、封闭的,外部的执行环境是访问不了的,但是闭包具有这个能力和权限。

闭包是一个函数,只不过这个函数有[超能力],可以访问到另一个函数的作用域。

「函数」和「自由变量」的总和,就是一个闭包。

'''
闭包的特点:
1. 存在内外层函数嵌套的情况
2. 内层函数引用了外层函数的变量或者参数(自由变量)
3.外层函数把内层的这个函数本身当做返回值进行返回
'''


def outer():
    print("outer")
    a = 1

    def inner():
        print("inner")
        nonlocal a  # 闭包是由于函数内部使用了函数外部的变量。这个函数对象不销毁,则外部函数的局部变量也不会被销毁。
        print(f"a={a}")  # a变成了自由变量 形成了闭包,因为需要使用

    return inner


inn = outer()  # inner的地址
print("------")
inn()

在这里插入图片描述

闭包的作用
作用1: 隐藏变量避免全局污染
作用2: 可以读取函数内部的变量

同时闭包使用不当,优点就变成了缺点:
缺点1:导致变量不会被垃圾回收机制回收,造成内存消耗
缺点2:不恰当的使用闭包可能会造成内存泄漏的问题

案例1:

  1. 使用全局变量实现变量自增,但污染了其他程序
# 需求:实现变量a 自增
# 通过全局变量,可以实现,但会污染其他程序
a = 10


def add():
    global a
    a += 1
    print("a:", a)


def print_ten():
    if a == 10:
        print("ten!")
    else:
        print("全局变量a,不等于10")


add()
add()
add()
print_ten()

  1. 定义局部变量,不污染,但无法递增
# 需求:实现变量a 自增#通过局部变量,不能实现递增
a = 10


def add():
    a = 10
    a += 1
    print("a:", a)


def print_ten():
    if a == 10:
        print("ten!")
    else:
        print("全局变量a,不等于10")


add()
add()
add()
print_ten()

在这里插入图片描述

  1. 定义局部变量,不污染,但无法递增
a = 10


def add():
    a = 10

    def increment():
        nonlocal a
        a += 1
        print("a:", a)

    return increment


def print_ten():
    if a == 10:
        print("ten!")
    else:
        print("全局变量a,不等于10")


increment = add()
increment()
increment()
increment()
increment()
increment()
print_ten()
print(f"全局变量a={a}")

在这里插入图片描述

案例2:用闭包实现不修改源码添加功能

# 装饰器基础,为函数添加功能
def outfunc(func):
    def infunc(*args, **kwargs):
        print("日志纪录 start...")
        func(*args, **kwargs)
        print("日志记录 end...")

    return infunc


def fun1():
    print("使用功能1")


def fun2(a, b, c):
    print("使用功能2", a, b, c)


fun1 = outfunc(fun1)
# 装饰器(闭包)
fun1()
fun2 = outfunc(fun2)
fun2(100, 200, 300)

在这里插入图片描述

5 内置函数

5.1 map()函数

map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回。

比如我们有一个函数f(x)=x^2,要把这个函数作用在一个list [1,2,3,4,5,6,7,8,9]上,就可以用map()实现如下:
在这里插入图片描述
当然,不需要map()函数,也可以计算出结果,写一个循环,实现代码如下:

def f(x):
    return x * x


L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
    L.append(f(n))
print(L)
# [1, 4, 9, 16, 25, 36, 49, 64, 81]

【示例】map高阶函数的使用案例

def f(x):
    return x * x


L = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(L))

#L = map(lambda n: n * n, [1, 2, 3, 4, 5, 6, 7, 8, 9])
#print(list(L))

【示例】map函数传入两个列表

L = map(f2, [1, 2, 3, 4], [10, 20, 30])
print(list(L))
# [11, 22, 33]

【示例】map函数传入两个列表(用匿名函数)

L = map(lambda x, y: x + y, [1, 2, 3, 4], [10, 20, 30])
print(list(L))

5.2 reduce()函数

reduce位于functools模块

reduce把一个函数作用在一个序列[x1,x2,x3…]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:reduce(f, [x1, x2, x3, x4])= f(f(f(x1, x2), x3), x4)
在这里插入图片描述
【示例】reduce实现对一个序列求和

from functools import reduce
def add(x, y):
    return x + y


sum = reduce(add, [1, 3, 5, 7, 9])
print(sum) # 25

5.3 filter()函数

内置函数 fiter0 用于过滤序列。filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
在这里插入图片描述
【示例】filter过滤列表,删掉偶数,只保留奇数

# 在一个list中,删掉偶数,只保留奇数
def is_odd(n):
    return n % 2 == 1


L = filter(is_odd, [1, 2, 4, 5])
print(list(L)) # [1, 5]

L = filter(lambda n: n % 2 == 1, [1, 2, 4, 5, 6, 7, 8, 9])
print(list(L)) # [1, 5, 7, 9]

【示例】filter序列中的空字符串删掉

def not_empty(s):
    return s and s.strip()

L = filter(not_empty, ['A', '', 'B', None, 'c', ''])
print(list(L)) # ['A', 'B', 'c']

 # 或者用匿名函数实现:
 L = filter(lambda s: (s and s.strip()), ['A', '', 'B', None, 'c'])
print(list(L)) # ['A', 'B', 'c']

5.4 sorted函数

排序算法,排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。

  1. 如果是数字,我们可以直接比较
  2. 如果是自定义对象呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。通常规定,对于两个元素x和y,如果认为x<y,则返回-1,如果认为x== y,则返回0,如果认为x>y,则返回1,这样,排序算法就不用关心具体的比较过程,而是根据比较结果直接排序。

【示例】sorted对list进行排序

sorter1 = sorted([1, 3, 6, -20, 34])
print("升序排列:", sorter1) # 升序排列: [-20, 1, 3, 6, 34]

sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序
【示例】sorted函数接收一个Key自定义排序

# 1 升序排列
sorter1 = sorted([1, 3, 6, -20, 34])
print("升序排列:", sorter1)  # 升序排列: [-20, 1, 3, 6, 34]
# sorted()函数也是高阶函数,它还可以接收一个key函数来实现自定义的排序
sorter2 = sorted([1, 3, 6, -20, -70], key=abs)
print("自定义排序:", sorter2)  # 自定义排序: [1, 3, 6, -20, -70]
sorter2 = sorted([1, 3, 6, -20, -70], key=abs, reverse=True)
print("自定义反向排序:", sorter2)  # 自定义反向排序: [-70, -20, 6, 3, 1]

# 2 字符串排序依照ASCII
sorter3 = sorted(["ABc", "abc", "p", "d"])
print("字符串排序:", sorter3)  # 字符串排序: ['ABc', 'abc', 'd', 'p']

# 3 忽略大小写排序
sorter4 = sorted(["ABc", "abc", "p", "d"], key=str.lower)
print("忽略字符串大小写排序:", sorter4)  # 忽略字符串大小写排序: ['ABc', 'abc', 'd', 'p']

# 4 要进行反向排序,不必改动key函数,可以传入第三个参数 reverse=True:
sorter5 = sorted(["ABc", "abc", "p", "d"], key=str.lower, reverse=True)
print("忽略字符串大小写反向排序:", sorter5)  # 忽略字符串大小写反向排序: ['p', 'd', 'ABc', 'abc']

【示例】sorted对自定义对象的排序
代码1:

class Student:
    def __init__(self, age, name):
        self.name = name
        self.age = age


def custom_sorted(stu1, stu2):
    if stu1.age < stu2.age:
        return -1
    if stul.age > stu2.age:
        return 1
    return 0


stul = Student(41, 'aaa')
stu2 = Student(21, 'ccc')
stu3 = Student(31, 'bbb')

student_list = sorted([stul, stu2, stu3], key=lambda x: x.age)  # 按照年龄排序
for stu in student_list:
    print('name:', stu.name, 'age:', stu.age)
    '''
        name: ccc age: 21
        name: bbb age: 31
        name: aaa age: 41
    '''

代码2:

from functools import cmp_to_key

class Student:
    def __init__(self, age, name):
        self.name = name
        self.age = age


def custom_sorted(stu1, stu2):
    if stu1.age < stu2.age:
        return -1
    if stul.age > stu2.age:
        return 1
    return 0


stul = Student(41, 'aaa')
stu2 = Student(21, 'ccc')
stu3 = Student(31, 'bbb')

student_list = sorted([stul, stu2, stu3], key=cmp_to_key(custom_sorted))
for stu in student_list:
    print('name:', stu.name, 'age:', stu.age)
    '''
        name: ccc age: 21
        name: bbb age: 31
        name: aaa age: 41
    '''
  • 20
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值