一等对象

编程语言理论家把“一等对象”定义为满足下述条件的程序实体:

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

把函数当作对象

函数都是function类的实例,类的方法是method类的实例(虽然经常会把方法称为函数)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def fun():
    pass

class Foo:
    def bar(self):
        pass

print(type(fun))
>>><class 'function'>
print(type(Foo().bar))
>>><class 'method'>

高阶函数

接受函数为参数或者把函数作为结果返回的函数称为高阶函数

在函数式编程范式中,最为人熟知的高阶函数有map(), filter(), reduce()

all()any()

all(iterable), 如果iterable的每个值都为True,返回True any(iterable), 如果iterable存在一个值为True,返回True

匿名函数

使用lambda关键字创建匿名函数,格式为lambda [arg1 [,arg2,.....argn]]:expression,匿名函数的定义体中不能赋值,不能使用whiletry语句。

我十分不喜欢用匿名函数,基本没有用过,即使是用高阶函数需要传递函数作为参数时,也是额外用def语句编写函数,所以一直对lambda理解不到位。

匿名函数这样理解:将arg x作为参数传递给expression,得到结果后返回。

Lundh提出的lambda表达式重构秘笈

  • (1)编写注释,说明lambda表达式的作用
  • (2)研究注释,用一个名称来概括注释
  • (3)把lambda表达式改写成def,使用那个名称作为函数名
  • (4)删除注释

可调用对象

使用callable()函数可判断是否为可调用对象。python中有7种可调用对象:

  • 用户使用deflambda创建的函数
  • 内置函数,使用C语言(CPython)实现的函数,如lentime.strftime
  • 使用C语言实现的方法,如dict.get
  • 方法,在类的定义体中定义的函数
  • 类,调用类时会运行__new__方法创建一个实例,python没有new运算符,所以调用类相当于调用函数
  • 定义了__call__()类的实例
  • 生成器函数,使用yield关键字的函数或方法
1
2
3
L = [abs, str, 13]
print([callable(x) for x in L])
>>>[True, True, False]

函数内省

函数内省(function introspection)是在运行时进行的一种对象检测机制,用来查看某个对象内部信息,如类的type类型、属性、方法、事件等等。
(内省: 文言,意思是查看内部情况。华夏特色的翻译,恕我直言,把第一个叫这个名字的人拉出去斩了)
python中使用dir()可以获得对象的各种信息。

1
2
3
4
5
def fun():
    pass

print(dir(fun))
>>>['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

获取关于参数的信息

以我目前水平不能理解作者在讲的这些内容有什么作用

函数注解

1
2
def clip(text:str, max_len:'int > 0'=80) -> str:
    return ''

函数声明中的各个参数可以在:之后增加注解表达式。如果参数有默认值,注解放在参数名和=号之间。如果想注解返回值,在)和函数声明末尾的:之间添加->和一个表达式。这个表达式可以是任何类型,在上面的例子中是str

注解不会做任何处理,只是存储在函数的__annotations__属性(一个字典)中

1
2
3
4
def clip(text:str, max_len:'int > 0'=80) -> str:
    return ''
print(clip.__annotations__)
>>>{'text': <class 'str'>, 'max_len': 'int > 0', 'return': <class 'str'>}

支持函数式编程的包

1
2
3
4
5
6
from functools import reduce, mul
def fact1(n):
    return reduce(lambda a, b: a*b, range(1, n+1))

def fact2(n):
    return reduce(mul, range(1, n+1))

operator模块为多个算术运算符提供了对应的函数,从而避免编写lambda a, b: a*b这种平凡的匿名函数

itemgetterattrgetter

operator模块中还有一类函数,能替代从序列中取出元素或读取对象属性的lambda表达式,如itemgetterattrgetter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 将students表按第二个属性升序排列
from operator import itemgetter
students = [
    ('xiao ming', 77, 180),
    ('lao wang', 75, 185),
    ('xiao qing', 100, 183),
]

res = sorted(students, key=itemgetter(1))
print(res)

attrgetter类似

methodcaller

methodcaller创建的函数会在对象上调用参数指定的方法

1
2
3
4
5
from operator import methodcaller
s = 'I am a pupil'
f = methodcaller('replace', ' ', '-')
print(f(s))
>>>I-am-a-pupil

functools.partial

1
2
3
4
5
from operator import mul
from functools import partial
f = partial(mul, 3)
print(f(7))
>>>21

固定第一个参数为3, 传入7则结果为21