Python 函数 高级内容

在 Python 中, 函数是一等对象。 编程语言理论家把“一等对象”定义为满足四个条件的程序实体:

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

在 Python 中, 整数、 字符串和字典都是一等对象。 后面的课程将重点讨论把函数作为对象的影响和实际应用。

看一个小小的例子:

def factorial(n):
    """
    returns n!
    :param n: 
    :return: 
    """
    return 1 if n < 2 else n * factorial(n-1)

print(factorial.__doc__)
print(type(factorial))

把函数视作对象,Python函数是对象,我们先创建一个函数,然后读取它的一个属性,最后确定这个函数本身是 function 类的一个实例。

接下来呢,展示了函数对象的“一等”本性。 我们可以把 factorial 函数赋值给变量 fact,
然后通过变量名调用。 我们还能把它作为参数传给 map 函数。 map 函数返回一个可迭代
对象, 里面的元素是把第一个参数(一个函数) 应用到第二个参数(一个可迭代对象, 这
里是 range(11)

fact = factorial
print(fact)
print(fact(5))
print(map(factorial, range(11)))
print(list(map(fact, range(11))))

有了一等函数或者说是一等对象, 就可以使用函数式风格编程。 函数式编程的特点之一是使用高阶函数。

什么是高阶函数呢?
接受函数为参数, 或者把函数作为结果返回的函数是高阶函数。

最常见的 sorted 函数就是 高阶函数。

我们做一个简单的例子,根据 字符串 的长度排序。

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

在函数式编程语言中,最常见的高阶函数有: map、 filter、 reduce, apply(在python3 中已经被删除了)。

三个问题:

  1. 将列表中每个元素扩大两倍。
  2. 保留列表中的奇数。
  3. 求列表中所有元素的和。

为了使用高阶函数, 有时创建一次性的小型函数更便利。 这便是匿名函数存在的原因。

lambda 关键字在 Python 表达式内创建匿名函数。

然而, Python 简单的句法限制了 lambda 函数的定义体只能使用纯表达式。 换句话
说, lambda 函数的定义体中不能赋值, 也不能使用 while 和 try 等 Python 语句。

如果使用 lambda 表达式导致一段代码难以理解,可以这样重构。

(1) 编写注释, 说明 lambda 表达式的作用。
(2) 研究一会儿注释, 并找出一个名称来概括注释。
(3) 把 lambda 表达式转换成 def 语句, 使用那个名称来定义函数。
(4) 删除注释。

可调用对象:

除了用户定义的函数, 调用运算符(即 ()) 还可以应用到其他对象上。 如果想判断对象能否调用, 可以使用内置的 callable() 函数。

Python 数据模型文档列出了 7 种可调用对象,

  1. 用户定义的函数
    使用 def 语句或 lambda 表达式创建。
  2. 内置函数
    使用 C 语言(CPython) 实现的函数, 如 len 或 time.strftime。
  3. 内置方法
    使用 C 语言实现的方法, 如 dict.get。
  4. 方法
    在类的定义体中定义的函数。

  5. 调用类时会运行类的 new 方法创建一个实例, 然后运行 init 方法, 初始化实例, 最后把实例返回给调用方。 因为 Python 没有 new 运算符, 所以调用类相当于调用函数。
  6. 类的实例
    如果类定义了 call 方法, 那么它的实例可以作为函数调用。
  7. 生成器函数
    使用 yield 关键字的函数或方法。 调用生成器函数返回的是生成器对象。生成器函数在很多方面与其他可调用对象不同,后面会细讲,生成器函数还可以作为协程,后面的课程会细讲。

Python 中有各种各样可调用的类型, 因此判断对象能否调用, 最安全的方法是使用内
置的 callable() 函数:

print([callable(obj) for obj in [int, 'str', str]])

如何让用户自定义一个可调用类型呢?
不仅 Python 函数是真正的对象, 任何 Python 对象都可以表现得像函数。 为此, 只需实现实例方法 call

import random


class BingoCage:
    def __init__(self, items):
        self._items = list(items) 
        random.shuffle(self._items) 
        
    def pick(self): 
        try:
            return self._items.pop()
        except IndexError:
            raise LookupError('pick from empty BingoCage') 
        
    def __call__(self): 
        return self.pick()
bingo = BingoCage(range(3))
print(bingo.pick())
print(bingo())
print(callable(bingo))

实现 call 方法的类是创建函数类对象的简便方式, 此时必须在内部维护一个状态,让它在调用之间可用, 例如 BingoCage 中的剩余元素。
装饰器就是这样。 装饰器必须是函数, 而且有时要在多次调用之间“记住”某些事 [ 例如备忘(memoization) , 即缓存消耗大的计算结果, 供后面使用 ]

创建保有内部状态的函数, 还有一种截然不同的方式——使用闭包。 闭包和装饰器会在后面讨论。 下一节课从 函数内省 开始讲解,说说有关函数属性的知识。

你可能感兴趣的:(Python 函数 高级内容)