目录
- Python 函数
- 头等对象
- 内部功能
- 作为返回值的函数
- Python 中的简单装饰器
- 结论
Python 函数
为了理解装饰器,您必须首先了解函数如何工作的一些细节。函数有很多方面,但在装饰器的上下文中,函数根据给定的参数返回一个值。下面是一个基本示例:
`>>> def add_one(number):
... return number + 1
...
add_one(2)
3`
一般来说,Python 中的函数也可能有副作用,而不仅仅是将输入转换为输出。print() 函数就是一个例子:它返回 None,同时具有将某些内容输出到控制台的副作用。但是,要理解装饰器,只需将函数视为将给定参数转换为值的工具就足够了。
头等对象
在函数式编程中,您几乎完全使用没有副作用的纯函数。虽然不是一种纯粹的函数式语言,但 Python 支持许多函数式编程概念,包括将函数视为一类对象。
这意味着函数可以传递并用作参数,就像任何其他对象(如 str、int、float、list 等)一样。请考虑以下三个功能:
`def say_hello(name):
return f"Hello {name}"
def be_awesome(name):
return f"Yo {name}, together we're the awesomest!"
def greet_bob(greeter_func):
return greeter_func("Bob")`
这里,和是常规函数,它们需要以字符串形式给出名称。但是,该函数需要一个函数作为其参数。例如,您可以向它传递 或 函数。say_hello()be_awesome()greet_bob()say_hello()be_awesome()
若要测试函数,可以在交互模式下运行代码。你用标志来执行此操作。例如,如果您的代码位于名为 的文件中,则运行:-igreeters.pypython -i greeters.py
`>>> greet_bob(say_hello)
'Hello Bob'
greet_bob(be_awesome)
'Yo Bob, together we're the awesomest!'`
请注意,它指的是两个函数,但方式不同:和 。该函数的命名不带括号。这意味着仅传递对函数的引用。该函数未执行。另一方面,该函数是用括号编写的,因此将像往常一样调用它。greet_bob(say_hello)greet_bob()say_hellosay_hellogreet_bob()
这是一个重要的区别,对于函数如何作为一类对象工作至关重要。不带括号的函数名称是对函数的引用,而带尾部括号的函数名称调用函数并引用其返回值。
内部功能
可以在其他函数中定义函数。这种函数称为内部函数。下面是一个具有两个内部函数的函数示例:
`def parent():
print("Printing from parent()")
def first_child():
print("Printing from first_child()")
def second_child():
print("Printing from second_child()")
second_child()
first_child()`
调用函数时会发生什么情况?想一想。然后在交互模式下运行以尝试一下。输出如下:parent()inner_functions.py
>>> parent() Printing from parent() Printing from second_child() Printing from first_child()
请注意,定义内部函数的顺序无关紧要。与任何其他功能一样,打印仅在执行内部功能时发生。
此外,在调用父函数之前,不会定义内部函数。它们的作用域为 ,这意味着它们仅作为局部变量存在于函数内部。尝试调用 。您会收到一个错误:parent()parent()first_child()
每当你调用时,内部函数也被调用。但是,由于它们的本地范围,它们在函数之外不可用。parent()first_child()second_child()parent()
作为返回值的函数
Python 还允许您从函数返回函数。在以下示例中,重写以返回其中一个内部函数:parent()
`def parent(num):
def first_child():
return "Hi, I'm Elias"
def second_child():
return "Call me Ester"
if num == 1:
return first_child
else:
return second_child`
请注意,您返回时没有括号。回想一下,这意味着您正在返回对函数的引用。相反,带括号是指计算函数的结果。您可以在以下示例中看到这一点:first_childfirst_childfirst_child()
`>>> first = parent(1)
second = parent(2)
first
<function parent..first_child at 0x7f599f1e2e18>
second
<function parent..second_child at 0x7f599dad5268>`
有点隐晦的输出意味着该变量引用了 中的局部函数,而指向 。firstfirst_child()parent()secondsecond_child()
现在,即使您无法直接访问它们指向的函数,也可以像使用常规函数一样使用它们:firstsecond
`>>> first()
'Hi, I'm Elias'
second()
'Call me Ester'`
您可以识别在 中定义的内部函数的返回值。parent()
最后,请注意,在前面的示例中,您在父函数中执行了内部函数,例如 .但是,在最后一个示例中,您没有在返回时向内部函数添加括号,例如 。这样,您就可以获得对将来可以调用的每个函数的引用。first_child()first_child
Python 中的简单装饰器
现在您已经了解了函数就像 Python 中的任何其他对象一样,您已经准备好继续前进,看看 Python 装饰器这个神奇的野兽。您将从一个示例开始:
`def decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
def say_whee():
print("Whee!")
say_whee = decorator(say_whee)`
在这里,您定义了两个正则函数、和 以及一个内部函数。然后,您重新定义以应用于原始 .decorator()say_whee()wrapper()say_whee()decorator()say_whee()
你能猜到当你打电话时会发生什么吗?在 REPL 中尝试一下。除了运行带有标志的文件外,您还可以手动导入函数:say_whee()-i
`>>> from hello_decorator import say_whee
say_whee()
Something is happening before the function is called.
Whee!
Something is happening after the function is called.`
要了解这里发生了什么,请回顾前面的示例。你正在应用你到目前为止所学到的一切。
所谓的装饰发生在以下一行:
say_whee = decorator(say_whee)
实际上,该名称现在指向内部函数。请记住,当您调用以下命令时,您将作为函数返回:say_wheewrapper()wrapperdecorator(say_whee)
>>> say_whee <function decorator.<locals>.wrapper at 0x7f3c5dfd42f0>
但是,它对原始函数的引用为 ,并且它在两次调用之间调用该函数。wrapper()say_whee()funcprint()
简单地说,装饰器包装一个函数,修改其行为。
在继续之前,请看第二个示例。由于是常规的 Python 函数,因此装饰器修改函数的方式可能会动态更改。为了不打扰你的邻居,下面的例子只会在白天运行修饰好的代码:wrapper()
`from datetime import datetime
def not_during_the_night(func):
def wrapper():
if 7 <= datetime.now().hour < 22:
func()
else:
pass # Hush, the neighbors are asleep
return wrapper
def say_whee():
print("Whee!")
say_whee = not_during_the_night(say_whee)如果您尝试在睡前打电话,则不会发生任何事情:say_whee()
>>> from quiet_night import say_whee
标签:函数,parent,whee,Python,say,child,装饰,def,入门 From: https://www.cnblogs.com/sdfcv/p/18018040say_whee()`
此处不打印任何输出。那是因为测试失败了,所以包装器没有调用 ,原来的 .say_whee()iffunc()say_whee()
结论
这是一段相当长的旅程!在本教程中,您首先仔细研究了函数,特别是如何在其他函数中定义它们并像传递任何其他 Python 对象一样传递它们。然后,您了解了装饰器以及如何编写它们
更多详情内容请参阅:Python 装饰器入门