首页 > 技术文章 > Python 学习笔记(二)

wilfredshen 2020-05-08 23:52 原文

date: 2019-09-07

我使用的Python版本为3.7,没有装配环境的同学可以到下方链接查看教程。

本期内容依旧是高阶函数的内容,接上期。

高阶函数

函数闭包

Python允许在函数中定义函数,内部函数无法在外部函数之外被调用,类似Java的内部类。

需要注意的一点是,内部函数调用外部函数的属性时,采用的是引用传值,这可能会带来一些问题:

def func():
    res = []
    for x in range(1, 4):
        def f():
            return x**2
        res.append(f)
    return res

for f in func():
    print(f())
# 9
# 9
# 9

没错,也许你觉得输出的应该是1、4、9,但是因为引用传值,在循环的最后x是3,所以它们的调用结果都是9。

这个特性在某些时候十分方便,而且它的麻烦很容易规避,只要避免将值留到以后调用的时候才处理就行。

匿名函数

对许多语言来说,匿名函数都是一个不可或缺的东西,它避免我们为了某些不常用的功能而专门去写一个函数,而是通过一行简洁的代码来完成。

Python中匿名函数的语法:lambda a, b: a if a > b else b,代码限制在一行之内。以lambda为起始标识;:左侧为参数,可以有任意个;右侧为函数的返回值,可以在其中使用变形的if语句等。

map函数上的简单应用:

itr = map(lambda x: x**2, [0, -9, 3])
print(list(itr))
# [0, 81, 9]

函数装饰器

这是一个稍显复杂的概念,主要是其代码比较绕,一般嵌套了多层内部函数,请耐下心来慢慢理解。

函数装饰器,顾名思义,是给函数添加一些新东西。比如在代码调试的过程中,我们需要输出一些调试信息,这时候在每个函数中都添加上一行代码显得十分繁琐,因为之后还要删除,装饰器就是为了解决这个烦恼。

基础装饰器

看以下代码,展示了一个最简单的装饰器:

def func(x):
    return x*x

def deco(f):
    def f2(x):
        print('call ' + f.__name__ + '()')
        return f(x)
    return f2

func = deco(func)
print(func(5))
# call func()
# 25

上述代码成功让func函数输出了调试信息,而没有更改func的代码。这里要注意的一点是,若其中的代码func = deco(func)改为f = deco(func),调用输出f()依旧是call func(),因为传入的函数为func,其相关属性并没有改变。

@语法

以上展示的是装饰器的初级应用,因为func = deco(func)这行代码与函数的定义分离了,不利于我们理解上下文,通过@语法我们可以在函数定义的时候就知道它添加的那些功能:

@deco
def func2(x):
    return x*x

print(func2(3))
# call func2()
# 9

参数传递

使用函数装饰器还有一个传递参数的问题,由于函数的参数个数不是固定的,在通过函数装饰器装饰之后,可能无法正常调用参数,所以在装饰器内传递参数时应统一传递两个参数:*args, **kw

带参数的装饰器

此处不必多讲,直接看代码就行:

import functools
from functools import reduce

def performance(words):
    def perf_decorator(f):
        def wrapper(*args, **kw):
            print('call %s() as %s' % (f.__name__, words))
            return f(*args, **kw)
        return wrapper
    return perf_decorator

@performance('test')
def factorial(n):
    return reduce(lambda x,y: x*y, range(1, n+1))

print(factorial(12))
# call factorial() as test
# 479001600

因为使用了传递参数的装饰器,所以需要多嵌套一层函数来传递参数。

偏参数

偏函数改变了原函数的调用方法。对于一些函数,经常会传递一些固定的参数,在不更改这些函数的源码的情况下,我们可以使用高阶函数来进行包装:

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

print(int2('10010'))
# 18

我们也可以采用functools中提供的方法:


import functools
int2 = functools.partial(int, base=2)

print(int2('10010'))
# 18

个人博客:https://wilfredshen.cn/

推荐阅读