0
点赞
收藏
分享

微信扫一扫

Python 函数式编程(四)

佳简诚锄 2021-09-28 阅读 28

一. 装饰器的应用

概念回顾

什么是装饰器?
装饰器是一个函数,主要作用是用来包装另一个函数或类。即在不改变原函数名或类名的情况下,改变被包装对象的行为。

函数装饰器及应用

函数装饰器?
函数装饰器的本质是一个闭包,接收一个函数作为参数,返回的是内部嵌套函数。

示例 1:函数装饰器的基本用法

def my_deco(fn):
    def fx():
        print("my_func函数被调用之前...")
        fn()
        print("my_func函数被调用之后...")
    return fx

@my_deco
def my_func():
    print("函数my_func被调用")

运行结果:

>> my_func()
my_func函数被调用之前...
函数my_func被调用
my_func函数被调用之后...

@my_deco 用来将 my_func 绑定装饰器函数 my_deco 的返回值。decorator 装饰器本身是一个闭包,接收一个函数,返回另一个函数。因此,函数一旦被装饰,真正调用的将会是装饰器函数的返回值(装饰器返回一个函数)。

示例 2:装饰器函数内部,嵌套函数外部的代码,只在装饰器初始化时执行一次

def my_deco(fn):
    print("装饰器函数被调用了...")
    fn()
    def fx():
        print("fx 被调用")
    return fx
 
@my_deco
def my_func():
    print("函数 my_func 被调用!")

运行结果:

装饰器函数被调用了...
函数 my_func 被调用!
>> my_func()
fx 被调用

@my_dec 装饰 my_func 时,装饰器函数 my_deco 被调用,打印 函数 my_func 被调用!。之后,由于 my_func 引用已经指向了装饰器函数my_deco 的返回值,再调用 my_func 时实则调用的都是 fx 函数了。

示例 3:在银行存取款功能基础上,添加身份验证

def identity_check(fn):
    def fx(name, x):
        print("正在进行身份验证...")
        fn(name, x)
    return fx
 
@identity_check
def save_money(name, x):
    print("{} 存钱 {} 元。".format(name, x))
 
 
@identity_check
def with_draw(name, x):
    print("{} 取钱 {} 元。".format(name, x))

运行结果:

>> save_money("Alex", 100)
正在进行身份验证...
Alex 存钱 100 元。
>> with_draw("Alex", 100)
正在进行身份验证...
Alex 取钱 100 元。

可见,装饰器在不改变原函数和调用者行为的情况下,可用来改变原有函数功能。

示例 4:在取钱函数上再添加一个装饰器,完成取钱后的短信通知功能

def identity_check(fn):
    def fx(name, x):
        print("正在进行身份验证...")
        fn(name, x)
    return fx
 
 
def send_message(fn):
    """再增加一个装饰器函数,在取钱后添加余额变动提醒功能"""
    def fy(name, x):
        fn(name, x)
        print("send message -¥{} changed.".format(x))
    return fy
 
 
@identity_check
def save_money(name, x):
    print("{} 存钱 {} 元。".format(name, x))
 
 
@send_message
@identity_check
def with_draw(name, x):
    print("{} 取钱 {} 元。".format(name, x))

运行结果:

>> save_money("Alex", 100)
正在进行身份验证...
Alex 存钱 100 元。
>> with_draw("Alex", 100)
正在进行身份验证...
Alex 取钱 100 元。
send message -¥100 changed.

@identity_check 先于 @send_message 装饰 with_draw ,故 identity_check 的返回函数将优先被调用。

二. 函数的文档字符串

函数的文档字符串是函数内第一个未赋值给任何变量的字符串,用来说明函数的功能和使用方法:

  • 函数的 __doc__ 属性用来记录函数的文档字符串;
  • 函数的 __name__ 属性用来记录函数的名称。
def hello():
    """这里是hello函数的文档字符串
    此函数用来打招呼
    """
    pass

运行结果:

>> print(hello.__name__)
hello
>> print(hello.__doc__)
这里是hello函数的文档字符串
    此函数用来打招呼
>> print(help(hello))
Help on function hello in module __main__:

hello()
    这里是hello函数的文档字符串
    此函数用来打招呼

None

三. 可变类型作为函数参数

示例 1:

L = [1, 2, 3]

def append_print(n=0, lst=[]):
    lst.append(n)
    print(lst)

运行结果:

>> append_print(4, L)
[1, 2, 3, 4]
>> append_print(5, L)
[1, 2, 3, 4, 5]
>> append_print(1)
[1]
>> append_print(1)
[1, 1]

list 属于可变类型,在传递过程中,将变量的引用进行传递,因此函数内部可以直接对全局作用域的可变类型进行修改。关于参数 lst 的默认值设定:lst=[] 在函数初始化时将在内存中创建一个空的列表,而每次调用append_print 时,都将修改此列表的内容。

可变数据类型作为函数参数时,若要指定默认值,应使用 lst=None 的形式,并在函数内部创建一个全新的列表:

L = [1, 2, 3]

def append_print(n=0, lst=None):
    if lst is None:
        lst = []
    lst.append(n)
    print(lst)

运行结果:

>> append_print(4, L)
[1, 2, 3, 4]
>> append_print(5, L)
[1, 2, 3, 4, 5]
>> append_print(1)
[1]
>> append_print(1)
[1]
举报

相关推荐

0 条评论