闭包(Closure)是指函数对象(函数)与其周围的环境(包括引用了外部变量的函数及其引用的变量)的组合。闭包可以访问并修改其词法作用域中的变量,即使在函数调用结束后,这些变量仍然可以被访问和修改。
在 Python 中,一个闭包是由一个嵌套函数和对其周围环境的引用组成。通常情况下,内部函数定义在外部函数的作用域内,并且内部函数引用了外部函数的变量。
下面是一个简单的例子来说明闭包的概念:
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
closure = outer_function(5)
print(closure(3))
在这个例子中,outer_function 是外部函数,它接收一个参数 x。inner_function 是内部函数,它定义在外部函数内部,并引用了外部函数的参数 x。
当调用 outer_function(5) 时,它返回了一个闭包 closure。closure 实际上是一个函数对象,可以调用并传入参数给 inner_function。
在上面的代码中,closure(3) 的结果是 8。即使 outer_function 调用已经结束,我们仍然可以访问和使用 inner_function 中引用的 x 变量。
闭包适用于需要在函数之间共享状态或记住特定上下文的情况。通过使用闭包,我们可以创建具有持久状态的函数对象,这对于某些编程问题是非常有用的。
注意点:
由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存。
Python 在嵌套函数内部查找变量时会按照以下顺序进行搜索:
def outer_function():
x = 10
def inner_function():
x = 20
print("Inner function:", x)
inner_function()
print("Outer function:", x)
outer_function()
在这个示例中,我们在内部函数 inner_function 中对变量 x 进行了赋值。
运行上面的代码将会得到以下输出:
Inner function: 20
Outer function: 10
我们可以看到,尽管在内部函数中修改了变量 x 的值,但外部函数中的变量 x 的值保持不变。这是因为在内部函数中使用了赋值操作,Python 将变量 x 视为一个新的本地变量,而不是对外部函数中的变量进行修改。
在 Python 中,当我们在一个嵌套函数内部定义一个同名的变量时,默认情况下,Python 会将这个变量视为一个新的本地变量,而不是引用外部的变量。这意味着,如果我们在嵌套函数中对这个变量进行修改,实际上只是修改了这个新的本地变量,而不是外部的变量。
使用 nonlocal 关键字可以改变这个默认行为,让嵌套函数能够访问并修改外部函数或者模块中的变量。
下面是一个示例来说明 nonlocal 的用法:
def outer_function():
x = 10
def inner_function():
# 关键代码
nonlocal x
x += 1
print("Inner function:", x)
inner_function()
print("Outer function:", x)
outer_function()
在这个示例中,inner_function 使用了 nonlocal 关键字来明确告诉 Python x 是来自于外部函数 outer_function 的变量。这样,在内部函数中对 x 的修改实际上是对外部函数中的 x 进行的修改。
运行上面的代码将会得到以下输出:
Inner function: 11
Outer function: 11
我们可以看到,内部函数成功地修改了外部函数的变量 x,并正确地打印出了增加后的值。
通过使用 nonlocal 关键字,我们能够更加灵活地访问和修改嵌套作用域中的变量,从而实现更加复杂的程序逻辑。
装饰器(Decorator)是 Python 中一种特殊的语法和语法糖,用于修改函数的行为或扩展函数的功能。它们允许我们在不修改原始函数定义的情况下,通过将其包装在其他函数中来添加额外的逻辑。
装饰器的语法如下:
@decorator
def function():
# 函数体
装饰器通过在函数定义前使用 @decorator 的语法糖来应用于特定的函数。这相当于将原始函数作为参数传递给装饰器函数,并将装饰器函数的返回值赋值给原始函数。这样,当我们调用原始函数时,其实是调用了被装饰后的函数。
2.1 不带参数和返回值的装饰器
下面是一个简单的示例来说明装饰器的用法:
def decorator_function(func):
def wrapper():
print("Before function execution")
func()
print("After function execution")
return wrapper
@decorator_function
def hello():
print("Hello, world!")
hello()
在这个示例中,我们定义了一个装饰器函数 decorator_function,它接受一个函数作为参数,并返回一个内部函数 wrapper。在 wrapper 函数内部,我们可以在调用原始函数之前和之后执行额外的逻辑。
通过在 hello 函数定义前使用 @decorator_function,我们将装饰器应用于 hello 函数。这样,当我们调用 hello 函数时,实际上会调用被装饰后的函数。
运行上面的代码将会得到以下输出:
Before function execution
Hello, world!
After function execution
我们可以看到,在调用 hello 函数之前和之后,装饰器函数 decorator_function 中定义的逻辑被执行了。
装饰器在实际开发中有很多应用场景,比如日志记录、性能分析、输入验证等。它们提供了一种简单而灵活的方式来修改函数的行为,同时保持代码的可读性和可维护性。
上面是无参数的装饰器使用, 带参数和不定参数的也可以使用.
如果要应用装饰器到有参数的函数上,我们需要使用一个更通用的装饰器模式。在这种情况下,我们需要定义一个装饰器函数,它接受任意个数和类型的参数,并返回一个内部函数,该内部函数代替原始函数的执行。
下面是一个示例来说明如何应用装饰器到有参数的函数上:
def decorator_function(func):
def wrapper(*args, **kwargs):
print("Before function execution")
result = func(*args, **kwargs)
print("After function execution")
return result
return wrapper
@decorator_function
def add(x, y):
return x + y
result = add(3, 4)
print("Result:", result)
在这个示例中,我们定义了一个通用的装饰器函数 decorator_function,它接受一个函数作为参数,并返回一个内部函数 wrapper。wrapper 函数使用 *args 和 **kwargs 来接受任意个数和类型的参数。
在 wrapper 函数内部,我们可以在调用原始函数之前和之后执行额外的逻辑。并且,我们使用 func(*args, **kwargs) 来调用原始函数,并将其参数传递给原始函数。
通过 @decorator_function 将装饰器应用于 add 函数,我们可以在调用 add 函数前后执行装饰器函数中的逻辑。
运行上面的代码将会得到以下输出:
Before function execution
After function execution
Result: 7
装饰器可以接受参数。这样的装饰器被称为带参数的装饰器。如果我们需要在装饰器中使用参数,那么我们需要在装饰器函数的外层定义一个接受参数的函数,然后在内层定义真正的装饰器函数。
这样做的原因是,通过这种多层的嵌套结构,我们可以在外层函数中接受装饰器的参数,并将这些参数传递给内层的装饰器函数。内层装饰器函数则负责接受被装饰的函数作为参数,并返回一个新的函数或类来替代原始的函数或类。
这样的设计使得装饰器可以非常灵活地处理不同的场景和需求,因为装饰器函数可以根据传递进来的参数来自定义装饰器的行为。
下面是一个示例展示如何定义带参数的装饰器:
# 定义外部函数接收参数
def repeat(n):
# 定义装饰器函数
def decorator(func):
# 定义内部函数用来代替原始函数
def wrapper(*args, **kwargs):
for _ in range(n):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 call() 方法,那么这个对象就是callable的。
class Test():
def __call__(self):
print('call me!')
t = Test()
t() # call me
类的装饰器使用和普通函数的装饰器使用方式是一样的, 只是@类名的方式代替@函数名的方式, 然后会在__init__方法接受参数
class Test(object):
def __init__(self, func):
print("---初始化---")
print("func name is %s"%func.__name__)
self.__func = func
def __call__(self):
print("---装饰器中的功能---")
self.__func()
@Test
def hello():
print("----test---")
hello()
输出结果:
---初始化---
func name is hello
---装饰器中的功能---
----test---
同样的如果要对待参数和返回值的函数使用类装饰器的话, 只需要修改下类的__call__方法,将参数添加上即可, 例如:
class Test(object):
def __init__(self, func):
print("---初始化---")
print("func name is %s" % func.__name__)
# 存储到对象中
self.__func = func
def __call__(self, *args, **kwargs):
print("---装饰器中的功能---")
# 调用目标函数,并获取返回值
result = self.__func(*args, **kwargs)
# 在功能后输出结果
print("Function result:", result)
return result
@Test
def hello(name):
print("Hello, %s!" % name)
return "Function execution complete"
result = hello("Alice")
print(result)
输出结果:
---初始化---
func name is hello
---装饰器中的功能---
Hello, Alice!
Function result: Function execution complete
Function execution complete
如果还想在接收装饰器的参数,那么可以通过在 Test 类中定义 init 方法来实现。
class Test(object):
def __init__(self, arg1, arg2):
# 接收装饰器参数
self.arg1 = arg1
self.arg2 = arg2
def __call__(self, func):
# 定义内部函数用来代替原始函数
def wrapper(*args, **kwargs):
print("---装饰器中的参数---")
print("arg1:", self.arg1)
print("arg2:", self.arg2)
print("---装饰器中的功能---")
# 调用目标函数
result = func(*args, **kwargs)
return result
# 返回装饰后的函数
return wrapper
@Test(arg1="value1", arg2="value2")
def hello(name):
print("Hello, %s!" % name)
return "Function execution complete"
result = hello("Alice")
print(result)
输出结果:
---装饰器中的参数---
arg1: value1
arg2: value2
---装饰器中的功能---
Hello, Alice!
Function execution complete
在 web 开发中,路由是指将 URL 请求映射到相应的处理函数或视图函数的过程。通过使用装饰器,我们可以实现根据不同的 URL 调用不同的处理函数或视图函数。
下面是一个简单的示例来说明如何使用装饰器实现路由功能:
# 定义一个字典,用于存储路由信息
routes = {}
# 定义外部函数接收路由参数
def route(path):
# 定义装饰器函数
def decorator(func):
routes[path] = func
# 定义接收函数参数的内部函数
def func2(file_name):
return func(file_name)
return func2
return decorator
# 定义路由处理函数
@route('/')
def index(file_name):
return "index.html"
@route('/about')
def about(file_name):
return f"{file_name}.html"
# 定义路由处理函数
def handle_route(path):
if path in routes:
return routes[path](path)
else:
return '404 Not Found'
# 使用路由处理函数处理请求
print(handle_route('/')) # 输出:index.html
print(handle_route('/about')) # 输出:/about.html
print(handle_route('/contact')) # 输出:404 Not Found
在这个示例中,route 是一个装饰器工厂函数,它接受路由路径作为参数。decorator 是真正的装饰器,它接受路由处理函数作为参数,并将路由路径和处理函数注册到 routes 字典中。而被装饰的函数被返回并保持不变。
通过使用 @route 语法将装饰器应用到函数上,可以将函数和对应的路由路径关联起来。然后,可以通过调用 handle_route 函数来根据路由路径执行相应的处理函数。如果路径在 routes 字典中,则执行对应的处理函数;否则返回 “404 Not Found”。