【Python笔记】Python的几个高级语法概念浅析:lambda表达式 && 闭包 && 装饰器

本文主要记录自己对几个高级语法概念的理解:匿名函数、lambda表达式、闭包、装饰器。

这几个概念并非Python特有,但本文只限于用Python做说明。

1. 匿名函数

匿名函数(anonymous function)是指未与任何标识符绑定的函数,多用在functional
programming languages领域,典型应用场合:

1) 作为参数传给高阶函数(higher-order function ),如python中的built-in函数filter/map/reduce都是典型的高阶函数

2) 作为高阶函数的返回值(虽然此处的"值"实际上是个函数对象)

与命名函数(named function)相比,若函数只被调用1次或有限次,则匿名函数在语法上更轻量级。

具体语法上,python通过lambda语法支持函数体为表达式的匿名函数,即:python的lambda表达式本质上是个匿名函数,但其函数体只能是个表达式,不能包含其它语句。

此外,高级动态语言常借助匿名函数实现闭包(closure)或装饰器(decorator)等高级语法。

在一些场合下,lambda表达式的使用使得python程序看起来非常简洁。例如,下面是根据value对dict元素做排序的代码示例:

>>> foo = {'father' : 65, 'mother' : 62, 'sister' : 38, 'brother' : 29, 'me' : 28}
>>> sorted(foo.iteritems(), key=lambda x: x[1])
[('me', 28), ('brother', 29), ('sister', 38), ('mother', 62), ('father', 65)]

2. 闭包

闭包(closure)本质上是一个包含了其引用环境(referencing
environment)的函数或函数引用,这里的"引用环境"通常由一张表来维护,该表存储了函数体会访问的非局部变量(non-local variables)的引用。

与C语言中的函数指针相比,闭包允许嵌套函数访问其作用域外的non-local变量,这与Python解释器对变量的作用域查找规则有关(Python支持LEGB的查找规则,想深究的话,可以参考<Learning Python>第4版第17章Scopes关于作用域及查找规则的详细讲解,或者查看这篇文章 做快速了解)。

对于运行时内存分配模型会在线性栈上创建局部变量的语言来说(典型如C语言),通常很难支持闭包。因为这些语言底层实现中,若函数返回,则函数中定义的局部变量均会随着函数栈被回收而销毁。但闭包在底层实现上要求其要访问的non-local变量在闭包被执行的时候保持有效,直到这个闭包的生命周期结束,这意外着这些non-local变量只有在其确定不再被使用时才能销毁,而不能随着定义这些变量的函数返回销毁。因此,天生支持闭包的语言通常采用garbage
collection的方式管理内存,因为gc机制保证了变量只有不再被引用时才会
由系统销毁并回收其内存空间

具体语法上,闭包通常伴随着函数嵌套定义。以Python为例,一个简单的闭包示例如下:

#!/bin/env python
#-*- encoding: utf-8 -*-

def startAt_v1(x):
    def incrementBy(y):
        return x + y
    print 'id(incrementBy)=%s' % (id(incrementBy))
    return incrementBy

def startAt_v2(x):
    return lambda y: x + y 

if '__main__' == __name__:
    c1 = startAt_v1(2)
    print 'type(c1)=%s, c1(3)=%s' % (type(c1), c1(3))
    print 'id(c1)=%s' % (id(c1))

    c2 = startAt_v2(2)
    print 'type(c2)=%s, c2(3)=%s' % (type(c2), c2(3))

执行结果如下:

id(incrementBy)=139730510519782
type(c1)=<type 'function'>, c1(3)=5
id(c1)=139730510519782
type(c2)=<type 'function'>, c2(3)=5

上述示例中,startAt_v1和startAt_v2均实现了闭包,其中:v1借助嵌套定义函数实现;v2则借助lambda表达式/匿名函数来实现。

我们以v1为例对闭包做说明:

1) 函数startAt_v1接受1个参数,返回1个函数对象,而这个函数对象的行为由嵌套定义的函数incrementBy实现。

2) 对函数incrementBy来说,变量x就是所谓的non-local变量(因为x既非该函数定义的局部变量,又非普通意义上的全局变量),incrementBy实现具体的函数行为并返回。

3) main入口的c1接收到的返回值是个函数对象,从id(incrementBy) == id(c1)可断定,c1"指向"的对象与函数名incrementBy"指向"的其实是同一个函数对象。

4) 受益于Python对闭包的支持,与普通函数的对象相比,c1指向的对象可以访问不在其函数作用域内的non-local变量,而这个变量是由incrementBy的外层包装函数startAt_v1的入参提供的,于是,相当于c1指向的函数对象对其外层包装函数的入参具有"记忆"功能,通过调用外层包装函数创建闭包时,不同的入参被内层函数作为引用环境维护起来。

5) 调用c1(3)时,传入的参数与引用环境维护的外层包装函数的参数一起运算得到最终结果。

以上步骤分析说明了一个闭包从创建到执行的基本原理,理解这个case后,闭包的概念也应该清晰了。

3. 装饰器

python支持装饰器(decorator)语法。装饰器的概念对于初学者来说比较晦涩,因为它涉及到函数式编程的几个概念(如匿名函数、闭包),这也是本文先介绍匿名函数和闭包的原因。

我们引用这篇文章对装饰器的定义:

A decorator is a function that takes a function object as an argument, and returns a function object as a return value.

从这个定义可知,装饰器本质上只是一个函数,它借助闭包的语法去修改一个函数(又称被装饰函数)的行为,即decorator其实是个闭包函数,该函数以被装饰函数名(这个函数名其实是一个函数对象的引用)作为入参,在闭包内修改被装饰函数的行为后,返回一个新的函数对象。

特别说明:decorator并非必须以函数形式出现,它可以是任何可被调用的对象,例如它也可以class形式出现,参见这篇文章给出的例子。

在定义好函数装饰器的前提下,当外部调用这个被装饰函数时,decorator的语法糖会由Python解释器解释为先执行装饰器函数,然后在装饰器返回的新函数对象上继续执行其余语句。

来个实例分析一下:

#!/bin/env python
#-*- encoding: utf-8 -*-

def wrapper(fn):
    def inner(n, m):
        n += 1
        print 'in inner: fn=%s, n=%s, m=%s' % (fn.__name__, n, m)
        return fn(n, m) + 6  // 这里有return且返回值为int对象
    return inner

@wrapper
def foo(n, m):
    print 'in foo: n=%s, m=%s' % (n, m)
    return n * m

print foo(2, 3)

上面的示例中,foo通过@wrapper语法糖声明它的装饰器是wrapper,在wrapper中,定义了嵌套的inner函数(该函数的参数列表必须与被装饰函数foo的参数列表保持一致),装饰器wrapper修改foo的行为后,返回inner(注意:由于inner的返回值是个int对象,故wrpper最终返回的也是个int对象)。

调用foo(2, 3)时,Python解释器先调用wrapper对foo做行为改写,然后返回int对象,不难推测,上述代码的执行结果如下:

in inner: fn=foo, n=3, m=3
in foo: n=3, m=3
foo(2, 3)=15

【参考资料】

1. wikipedia: Anonymous function

2. wikipedia: Closure (computer programming)

3. Chapter 17 of Book: Learning Python - Powerful Object-Oriented Programming, 4th edition

4. A beginner‘s guide to Python‘s namespaces, scope resolution, and the LEGB rule

5. Python Decorators

6. Understanding Python Decorators in 12 Easy Steps!

7. Decorators I: Introduction to Python Decorators

============= EOF =================

时间: 2024-08-14 01:56:29

【Python笔记】Python的几个高级语法概念浅析:lambda表达式 && 闭包 && 装饰器的相关文章

[Python笔记]Python学习笔记三

Python高阶函数 filter() 用于过滤序列 第一个参数接收一个函数,根据返回值来决定是否保留, True则留下,False则丢弃 def is_odd(n): return n%2 == 1 filter( is_odd, range(1,16) ) def not_empty(s): return s and s.strip() filter( not_empty, [ 'A', ' ', 'B ', None, ' ' ]) 内置sorted()函数用于列表排序 返回函数 将函数作

python 函数名 、闭包 装饰器 day13

1,函数名的使用. 函数名是函数的名字,本质就是变量,特殊的变量.函数名()加括号就是执行此函数. 1,单独打印函数名就是此函数的内存地址. def func1(): print(555) print(func1) # <function func1 at 0x0000027B7CF1A048> 2,函数名的赋值 def func(): print(666) f = func print(f()) 3,函数名可以作为容器类数据的元素. def f1(): print(111) def f2()

Python学习之旅 —— 基础篇(四)内置函数、装饰器

本篇要点: 内置函数 装饰器 一.内置函数 # abs() :取绝对值 >>> res = abs(-10) >>> print(res) 10 # 0,None,"",{},[],() 布尔值都为空 # all(),any() 判断值的真假 # all() 所有为真才为真 p = all([0,1,2,3]) # 0为False,所有p 是False print(p) # any() 任意一个为真即为真 p = any((0,""

Python之旅的第28天(描述符、类的装饰器)

周末真好,时间充裕,都能按照要求自己练习,感觉就是好 一.描述符 上次针对描述符的内容,理解的非常不到位,所以我就特地找了新的内容好好看了看,总结一下就是下面这些 # 前天我大概知道类描述符的一些特性,以及什么是数据描述符和非数据描述符 # 今天白天没事琢磨了一下,顺便又看了看各种案例,貌似理解更深入了一些 # 数据描述符:就是这个类属性是由一个实现了__get__(),__set__(),__delete__()中方法的新式类搞定了 # 非数据描述符则是,没有__set__方法的 # 当时没有

[Python笔记]Python学习笔记四

模块 在Python中,一个.py文件就是一个模块(Module) 使用模块的好处就是大大提高代码的可维护性,并且可以被其他地方引用. 同时可以避免函数名和变量名的冲突. Package(包) 每个包目录下面必须有一个 __init__.py文件 这个文件可以是空的,这个文件对应模块名就是当前目录名即包名 模块的使用 Python内置了很多模块 #!/usr/bin/env python # -*- coding:utf-8 -*- # 编写一个属于自己的模块 # 文件的第一个字符串被视为模块的

python笔记-python编程优化:常用原则和技术介绍

本人翻译自<Exper Python Programming> 'Premature optimization is the root of all evil in programming' -Donald Knuth    优化的三原则      让它跑起来先 一个非常常见的错误就是在编写代码之初我们就开始对代码进行优化.让人伤心的是这通常做的都是无用功,很多软件只有你跑起来了才会发现它真正的瓶颈在哪里. 站在用户的视角看问题 曾经有一个团队为了自己的项目跑起来更快加班加点最终达到了自己满意

python笔记——python数据类型

一.数据类型 1.核心数据类型: 数字:int, long(python3.5已经没有), float, complex, bool 字符:str, unicode 列表:list 字典:dict 元组:tuple 集合:set(可变集合),frozenset(不可变集合) 文件:file 二.数字类型 python的数字字面量:整数,布尔型,浮点数,复数,所有数字类型均为不可变 数字操作:+ , -, *, /, //, **, %, -x, +x 三.列表 列表是一系列按照特定顺序排列的元素

python之路基础-(二)内置函数、函数、装饰器

内置函数 python内置了以下函数,可以根据情况来使用 一.数学相关函数 divmod():取商和余数 >>> divmod(99,10) (9, 9) abs():取绝对值 >>> abs(-10) 10 len():查看序列长度 >>>list = [11,22,33,44] >>>r = len(list) >>>print(r)4 二.功能相关函数chr():在ascii码表中根据数字找出对应的字母 >

python 闭包 装饰器

闭包:是由函数和其他相关的引用环境组合而成的实体. 如果一个函数内部,对在外部作用域的变量进行引用,那么内部函数就被认为是闭包(closure). A CLOSURE is a function object that remembers values in enclosing scopes regardless of whether those scopes are still present in memory. >>> def hellocounter(name): count=[