函数是Python的一等对象?

在Python中,函数是一等对象。编程语言中的一等对象的定义:

  • 在运行时创建
  • 能赋值给变量或数据结构中的元素
  • 能作为参数传递给函数
  • 能作为函数的返回结果

高阶函数

我们把接受函数为参数或者把函数作为结果返回的函数称为高阶函数。比如常见的内置函数sorted:通过可选的key参数提供一个函数,应用到每一项上进行排序,如果想根据单词的长度排序,只需把len函数作为参数传给key即可。

fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
print(sorted(fruits, key=len))  # ['fig', 'apple', 'cherry', 'banana', 'raspberry', 'strawberry']

在函数式编程范式中,最为熟知的高阶函数有map,filter,reduce。虽然说这三个高阶函数有时还会用到,但是大多数场景中有更好的替代品。

在Python3中,map和filter还是内置函数,但是由于引入了列表推导式和生成器表达式,二者就变得没那么重要了。列表推导式或生成器表达式兼具map和filter这两个函数的功能,而且可读性也更高。

def add5(a):
    return a + 5

print(list(map(add5, range(6))))  # [5, 6, 7, 8, 9, 10]
print([add5(i) for i in range(6)])  # [5, 6, 7, 8, 9, 10]
print(list(map(add5, filter(lambda x: x % 2, range(6)))))  # [6, 8, 10]
print([add5(i) for i in range(6) if i % 2])  # [6, 8, 10]

reduce函数在Python3中把它放到了functools模块里面,这个函数最常用于求和,但是内置函数sum在执行求和操作时效果更好,在可读性和性能方面也很出色。

内置的归约函数还有all和any

all(iterable):iterable中没有表示Flase的元素时返回True。all([])返回True

any(iterable):只要iterable中有表示True的元素就返回True。any([])返回Flase

匿名函数

lambda关键字用于创建匿名函数。受Python语法限制,匿名函数的主体只能是一个纯粹的表达式,也就是说,匿名函数的主体中不能有while,try等Python语句,使用=赋值也是语句,也不能出现在匿名函数中。对于复杂度高,可读性低的匿名函数,建议重构,改写成def常规函数。

在高阶函数的参数列表中最适合使用匿名函数,例如下面翻转排序的代码

fruits = ['strawberry', 'fig', 'apple', 'cherry', 'raspberry', 'banana']
sorted_fruits = sorted(fruits, key=lambda w: w[::-1])
print(sorted_fruits)

除了作为参数传递给高阶函数,Python很少使用匿名函数。

lambda句法只是语法糖,lambda表达式会像def语句一样创建函数对象。

自定义可调用类型

判断一个对象是否可调用,最安全的方法是使用内置函数callable()

不仅Python函数是真正的对象,而且任何Python对象都可以表现的像函数,只需实现实例方法__call__即可

例如下面自定义一个可调用类型,从打乱的列表中取出元素

import random

class BingoCage:
    def __init__(self, item):
        self._item = list(item)
        random.shuffle(self._item)

    def pick(self):
        try:
            return self._item.pop()
        except IndexError:
            raise LookupError('no data')

    def __call__(self, *args, **kwargs):
        return self.pick()

bingo = BingoCage(range(6))
print(bingo())  # 2 
print(bingo())  # 4
print(callable(bingo))  # True

参数处理机制

Python函数最好的功能之一就是提供了极为灵活的参数处理机制。与之密切相关的是函数可以使用*和**拆包可迭代对象,映射各个参数。

def tag(name, *content, class_=None, **attrs):
    print(name, content, class_, attrs)


tag('br')  # br () None {}
tag('p', 'hello')  # p ('hello',) None {} # 除了name,后面的位置参数被content捕获,存入元祖
tag('p', 'hello', 'world')  # p ('hello', 'world') None {}
tag('p', 'hello', id=33)  # p ('hello',) None {'id': 33} # 除了class_,后面的关键字参数被attr捕获,存入字典
tag('p', 'hello', 'world', class_='sidebar')  # p ('hello', 'world') sidebar {}
my_tag = {'name': 'img', 'title': 'Sunset Boulevard', 'src': 'sunset.jpg', 'class': 'framed'}
tag(**my_tag)  # img () None {'title': 'Sunset Boulevard', 'src': 'sunset.jpg', 'class': 'framed'}

仅限关键字参数是Python3新增的功能。定义函数时,如果想指定仅限关键字参数,就要把它们放到有*的参数后面。如果不想支持数量不定的位置参数,但是想支持仅限关键字参数,则可以在签名中放一个*

def f(a, *, b):
    print(a + b)

f(1, b=2)  # 正确,因为b仅限关键字参数
f(1, 2)  # 出错:TypeError: f() takes 1 positional argument but 2 were given

从Python3.8开始,用户定义的函数签名可以指定仅限位置参数。比如说内置函数divmod(a,b)只能使用位置参数调用,不能写成divmod(a=10,b=4)。

def divmod(a, b, /): 
    return (a // b, a % b)

如果想定义只接受位置参数的函数,则可以在参数列表中使用/。/左边均是仅限位置参数,在/的后边,可以指定其他参数,处理方式一同往常。

支持函数式编程的包

operator 模块

operator 是一个标准库模块,它提供了一组函数,这些函数为各种内置的Python运算符提供了功能等效的函数形式。这些函数可用于自定义数据类型的操作,或在需要动态选择操作时使用。

以下是operator模块中的一些主要函数和它们的描述:

算术运算符:
operator.add(a, b): 返回a + b
operator.sub(a, b): 返回a - b
operator.mul(a, b): 返回a * b
operator.truediv(a, b): 返回a / b
operator.floordiv(a, b): 返回a // b
operator.mod(a, b): 返回a % b
operator.pow(a, b): 返回a ** b
比较运算符:
operator.eq(a, b): 返回a == b
operator.ne(a, b): 返回a != b
operator.lt(a, b): 返回a < b
operator.le(a, b): 返回a <= b
operator.gt(a, b): 返回a > b
operator.ge(a, b): 返回a >= b
逻辑运算符:
operator.not_(a): 返回not a
operator.and_(a, b): 返回a and b
operator.or_(a, b): 返回a or b
operator.xor(a, b): 返回a xor b
operator.is_(a, b): 返回a is b
operator.is_not(a, b): 返回a is not b
其他常用函数:
operator.itemgetter(*items): 创建一个从对象中获取项的函数。
operator.attrgetter(*attrs): 创建一个从对象中获取属性的函数。
operator.methodcaller(name, *args, **kwargs): 创建一个调用对象方法的函数。

使用operator模块可以使代码更加简洁和可读,特别是当你需要在函数或lambda表达式中动态选择或应用运算符时。

使⽤ functools.partial 冻结参数

functools.partial是 Python 的 functools标准库中的一个函数,它允许你“冻结”某个函数的部分参数,从而创建一个新的函数。这在需要多次调用同一函数但其中一些参数保持不变的情况下非常有用。

import operator
from functools import partial
add_five = partial(operator.add,5)
print(add_five(10))  # 15

上述代码使用了operator.add方法,原本需要传递两个参数给add,现在使用partial函数,“冻结”了某个参数,创建了一个新函数,从而只需要传递一个值即可。还不清楚的话,看下面自定义的案例:

from functools import partial

def multiply(a, b, c):
    return a * b * c

one_multiply = partial(multiply, c=4)
double_multiply = partial(multiply, b=3, c=4)
one_res = one_multiply(a=2, b=3)
double_res = double_multiply(a=1)
print(one_res)  # 24
print(double_res)  # 12

使用 functools.partial 时,可以为函数的任意位置或关键字参数设置默认值。创建的新函数可以像原始函数一样被调用,只是少了一些参数。为了使代码更加清晰和可读,通常建议注释或文档中提到 functools.partial 创建的新函数有哪些默认参数。

 

 

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值