美文网首页pythonpythonPython
python 头等对象之一,python 函数那些不一般的用法

python 头等对象之一,python 函数那些不一般的用法

作者: 梦想橡皮擦 | 来源:发表于2021-10-13 14:15 被阅读0次

    本篇博客为你带来 python 函数相关知识点的回顾。

    函数是 python 语言中一个非常重要的概念,并且是头等对象,可以把函数分配给变量,也可以将其作为参数传递给其它函数,当然作为其它函数的返回值也是可以的。

    函数就是对象

    函数是对象,所以可以将其赋值给一个变量

    def func():
        print("hello 橡皮擦")
    
    
    my_var = func  # 注意没有括号
    my_var()  # 调用
    

    函数是对象,所以可以出现在其它数据结构中,例如列表
    函数可以作为列表的项。

    def func():
        print("hello 橡皮擦")
    
    
    my_list = [func, 1, 2, 3]  # 列表第一项是函数
    print(my_list)
    my_list[0]()  # 列表第一项可以调用
    

    函数是对象,所以可以作为其它函数的参数

    def func():
        print("hello 橡皮擦")
    
    
    def func1(func):
        func()  # 调用参数
    
    
    func1(func)  # 函数作为参数
    

    可以接受其它函数作为参数的函数,称为高阶函数,这与我们之前学习的函数式编程又产生了关联。

    函数是对象,所以可以作为其它函数的返回值

    函数作为返回值,又衍生出来一个概念,函数的嵌套(内部函数)

    def func():
        def inner_func():
            print("内部函数")
    
        return inner_func
    
    
    print(func())  # 输出 <function func.<locals>.inner_func at 0x00000000028581E0>
    
    func()()  # 输出内部函数
    

    如果函数作为返回值,那调用外层函数只能得到一个函数对象,因此会出现 func()() 这样奇怪的写法。

    函数的嵌套还衍生一个概念,叫做 词法闭包,一般叫做 闭包,其核心内容是内部函数可以访问父函数中的参数。

    def func():
        f_name = "橡皮擦"
    
        def inner_func():
            print("内部函数")
            print(f_name)
    
        return inner_func
    
    
    inner = func()
    
    inner()  # 输出内部函数与橡皮擦
    

    python 中小小的装饰器

    学习装饰器的前提是掌握 函数是头等对象

    装饰器是用来 包装 函数的,它可以在不修改原函数的前提下,在被包装函数的前后增加代码。

    最简单的包装如下,给一个函数增加运行时间。

    def func():
        for i in range(100000):
            pass
    
    
    def time_decorator(func):
        import time
        start_time = time.perf_counter()
        func()
        print(f"{func.__name__} 运行时间为:", time.perf_counter() - start_time)
    
    # 装饰 func 函数
    time_decorator(func)
    

    上述代码中 time_decorator 就是一个装饰器,func 函数作为参数传递给它,从而实现代码运行时间的获取。

    使用 @函数名 可以更加方便的调用装饰器。

    def time_decorator(func):
        import time
        start_time = time.perf_counter()
        func()
        print(f"{func.__name__} 运行时间为:", time.perf_counter() - start_time)
    
    
    @time_decorator
    def func():
        for i in range(100000):
            pass
    

    使用 @ 语言,会立即装饰该函数,如果你想要调用 func() ,此时会出现如下错误:

    Traceback (most recent call last):
      File "E:/xxxx/18.py", line 13, in <module>
        func()
    TypeError: 'NoneType' object is not callable
    

    如果希望原函数还可以访问,需要在装饰器函数中嵌套内部函数,并将其返回。

    def time_decorator(func):
        import time
        # 嵌套函数
        def wrapper():
            start_time = time.perf_counter()
            func()
            print(f"{func.__name__} 运行时间为:", time.perf_counter() - start_time)
        # 返回内部函数
        return wrapper
    
    
    @time_decorator
    def func():
        for i in range(100000):
            pass
    
    
    func()
    

    一个函数可以被多个装饰器装饰,效果从下到上运行
    从下到上理解为接近函数的装饰器先运行。

    # 加粗标签装饰器
    def b(func):
        def wrapper():
            return f'<b>{func()}</b>'
    
        return wrapper
    
    
    # 段落标签装饰器
    def p(func):
        def wrapper():
            return f'<p>{func()}</p>'
    
        return wrapper
    
    
    @p
    @b
    def my_func():
        return "橡皮擦"
    
    
    print(my_func())
    

    装饰器由于词法闭包,会隐藏原函数的名称、文档字符串、参数列表
    为了解决该问题,需要导入 functools 模块中的 wraps 函数,先测试一下被装饰器修饰之后的函数相关信息。

    def title(func):
        def wrapper():
            """装饰器"""
            return func().title()
    
        return wrapper
    
    
    @title
    def say():
        return "hello world"
    
    
    h = say()
    print(h)  # 被装饰之后,单词首字母大写。
    print(say.__name__)  # 输出其函数名
    print(say.__doc__)  # 文档字符串
    

    对应的输出内容如下:

    Hello World
    wrapper
    装饰器
    

    可以发现 say 函数的函数名与文档字符串都已经被修改,如果不希望上述现象产生,使用 functools 模块的 wraps 函数即可。

    from functools import wraps
    def title(func):
        @wraps(func) # 复制信息到装饰器
        def wrapper():
            """装饰器"""
            return func().title()
    
        return wrapper
    
    
    @title
    def say():
        """say 函数文档字符串"""
        return "hello world"
    
    
    h = say()
    print(h)  # 被装饰之后,单词首字母大写。
    print(say.__name__)  # 输出其函数名
    print(say.__doc__)  # 文档字符串
    

    对应输出修改为下述内容:

    Hello World
    say
    say 函数文档字符串
    

    装饰器中的参数
    在编写装饰器的时候,经常碰到被装饰的函数具有参数情况,实操过程中使用 python 变长参数 *** 特性,即可解决该问题。
    如果函数只有一个参数,直接在 wrapper 声明即可

    def title(func):
        @wraps(func)  # 复制信息到装饰器
        def wrapper(name):
            """装饰器"""
            return func(name).title()
    
        return wrapper
    
    
    @title
    def say(name):
        """say 函数文档字符串"""
        return name + "hello"
    
    
    h = say("橡皮擦")
    print(h)
    

    *多个参数就比较繁琐了,所以位置参数直接使用 *args,关键字参数使用 *kwargs

    from functools import wraps
    
    
    def title(func):
        @wraps(func)  # 复制信息到装饰器
        def wrapper(*args):
            """装饰器"""
            return func(*args).title()
    
        return wrapper
    
    
    @title
    def say(name, age):
        """say 函数文档字符串"""
        return name + " hello " + " age " + str(age)
    
    
    h = say("橡皮擦",18)
    print(h)
    

    下述代码为关键字参数测试代码:

    from functools import wraps
    
    
    def title(func):
        @wraps(func)  # 复制信息到装饰器
        def wrapper(*args,**kwargs):
            """装饰器"""
            return func(*args,**kwargs).title()
    
        return wrapper
    
    
    @title
    def say(name):
        """say 函数文档字符串"""
        return name + " hello "
    
    
    h = say(name="橡皮擦")
    print(h)
    

    总结一下:

    • *args:用来收集额外的位置参数,组成元组;
    • **kwargs:用来收集关键字参数,组成字典。

    其中 argskwargs 非固定名称,你可以根据需要进行设置,不过行业里面其它程序员都遵守该规范。

    写在后面

    以上内容就是本文的全部内容,希望对学习路上的你有所帮助~

    更多精彩

    相关文章

      网友评论

        本文标题:python 头等对象之一,python 函数那些不一般的用法

        本文链接:https://www.haomeiwen.com/subject/igosoltx.html