写点什么

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

发布于: 刚刚
python 头等对象之一,python 函数那些不一般的用法

本篇博客为你带来 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_decoratordef 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_decoratordef 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@bdef my_func(): return "橡皮擦"

print(my_func())
复制代码


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


def title(func):    def wrapper():        """装饰器"""        return func().title()
return wrapper

@titledef say(): return "hello world"

h = say()print(h) # 被装饰之后,单词首字母大写。print(say.__name__) # 输出其函数名print(say.__doc__) # 文档字符串
复制代码


对应的输出内容如下:


Hello Worldwrapper装饰器
复制代码


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


from functools import wrapsdef title(func):    @wraps(func) # 复制信息到装饰器    def wrapper():        """装饰器"""        return func().title()
return wrapper

@titledef say(): """say 函数文档字符串""" return "hello world"

h = say()print(h) # 被装饰之后,单词首字母大写。print(say.__name__) # 输出其函数名print(say.__doc__) # 文档字符串
复制代码


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


Hello Worldsaysay 函数文档字符串
复制代码


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


def title(func):    @wraps(func)  # 复制信息到装饰器    def wrapper(name):        """装饰器"""        return func(name).title()
return wrapper

@titledef 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

@titledef 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

@titledef say(name): """say 函数文档字符串""" return name + " hello "

h = say(name="橡皮擦")print(h)
复制代码


总结一下:


  • *args:用来收集额外的位置参数,组成元组;

  • **kwargs:用来收集关键字参数,组成字典。


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

发布于: 刚刚阅读数: 2
用户头像

爬虫 100 例作者,蓝桥签约作者,博客专家 2021.02.06 加入

6 年产品经理+教学经验,3 年互联网项目管理经验; 互联网资深爱好者; 沉迷各种技术无法自拔,导致年龄被困在 25 岁; CSDN 爬虫 100 例作者。 个人公众号“梦想橡皮擦”。

评论

发布
暂无评论
python 头等对象之一,python 函数那些不一般的用法