【Python高级编程深度剖析】:生成器、装饰器和上下文管理器的秘密
发布时间: 2024-12-27 22:57:34 阅读量: 6 订阅数: 13
vb人事管理系统全套(源代码+论文+开题报告+实习报告)(2024zq).7z
![【Python高级编程深度剖析】:生成器、装饰器和上下文管理器的秘密](https://blog.finxter.com/wp-content/uploads/2022/12/image-180-1024x576.png)
# 摘要
本文深入探讨了Python编程语言中的高级特性,包括生成器、装饰器和上下文管理器的设计与实现。首先介绍了生成器的内部机制、创建方法和在并发编程以及大数据处理中的优化策略。接着,文章分析了装饰器的理论基础、高级技巧和在框架开发中的应用。此外,本文阐述了上下文管理器的工作原理、自定义方法及在资源管理和同步中的实践应用。最后,综合应用了这些特性,并通过案例分析讨论了它们在实际项目中的优势与局限。文章还探讨了高级编程思维与最佳实践,以及Python的未来发展方向,旨在提升开发者对Python高级特性的理解和应用能力,帮助他们编写更高效、更可维护的代码。
# 关键字
Python高级特性;生成器;装饰器;上下文管理器;并发编程;代码最佳实践
参考资源链接:[python实验报告全(附代码)](https://wenku.csdn.net/doc/6412b550be7fbd1778d42b61?spm=1055.2635.3001.10343)
# 1. Python高级特性概述
Python作为一门广泛应用于IT行业的高级编程语言,其高级特性为开发者提供了高效编程的利器。在本章中,我们将简要介绍这些高级特性,为后续章节的深入探讨奠定基础。本章内容包括但不限于Python函数的高级用法、数据结构的高级操作以及与性能优化相关的高级技术点。
首先,Python函数高级用法涵盖了默认参数、关键字参数和可变参数列表等概念,这些都是构建灵活函数接口的关键。接下来,我们会探讨Python内置的数据结构如列表、字典和集合的高级操作,包括推导式和切片等。
然后,本章还将涉及到一些特定的编程模式和概念,比如装饰器、上下文管理器以及生成器。这些特性允许开发者编写更加简洁和高效的代码,同时还有助于提升代码的可读性和可维护性。
通过对这些高级特性的概览,我们可以发现Python不仅是一门易学易用的语言,更是一门具有强大表现力和灵活性的编程语言。这为IT行业提供了无限的可能性和创新的工具。接下来的章节,我们将详细深入这些高级特性,探索其背后的工作原理和最佳实践。
# 2. 生成器的内部机制与应用
### 2.1 生成器的基本概念与创建
#### 2.1.1 yield关键字的使用与原理
`yield`是Python中的一个关键字,它使得一个函数成为生成器函数。当你调用生成器函数时,它会返回一个生成器对象,而不是直接执行函数体。生成器对象本身是一个迭代器,可以逐个产生函数中的值。这使得它成为处理大量数据或者无限序列的理想选择,因为不需要一次性将所有数据加载到内存中。
当生成器函数执行到一个`yield`语句时,函数会暂停执行并将`yield`表达式的值返回给调用者。随后,生成器函数的执行状态被保存起来,包括局部变量和程序计数器等。当调用生成器对象的`__next__()`方法(在Python 3中)或`next()`函数时,函数会从上次暂停的`yield`语句处继续执行,直到遇到下一个`yield`表达式或函数执行完毕。
以下是一个使用`yield`关键字的简单例子:
```python
def countdown(n):
while n > 0:
yield n
n -= 1
counter = countdown(5)
print(next(counter)) # 输出: 5
print(next(counter)) # 输出: 4
```
在上述代码中,`countdown`函数是一个生成器函数,它逐个产生一个从n到0的递减序列。每次调用`next(counter)`时,函数继续执行直到下一个`yield`语句,返回一个值然后暂停。
#### 2.1.2 生成器表达式的构建与应用
生成器表达式提供了一种类似于列表推导式的简洁语法,但是它不会立即创建一个完整的列表,而是返回一个生成器对象。这意味着生成器表达式具有延迟计算的特性,只有在迭代生成器时才会计算每个元素的值。
生成器表达式的一般形式为:
```python
(gexp for item in iterable if condition)
```
其中`gexp`是表达式,`item`是迭代变量,`iterable`是可迭代对象,`condition`是一个可选的条件表达式,用于过滤元素。
例如,以下代码生成了一个从0到9的数字序列的生成器:
```python
numbers = (x for x in range(10))
print(list(numbers)) # 输出: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
```
在这个例子中,我们创建了一个生成器对象`numbers`,并将其转换为列表以便打印。需要注意的是,一旦生成器被消耗完毕,再次调用`next(numbers)`将引发`StopIteration`异常。
### 2.2 生成器的进阶用法
#### 2.2.1 生成器与迭代器的关系
生成器是迭代器的一种特殊形式,但它们之间有一些重要的区别。所有生成器都是迭代器,因为生成器实现了迭代器协议,即定义了`__next__()`方法。然而,并非所有的迭代器都是生成器,比如列表、元组和字典等内置的可迭代对象,它们不是生成器,但可以产生迭代器。
生成器提供了一种惰性求值的方式,只有在迭代过程中才会计算每个值。这意味着生成器可以用来处理无限序列或者非常大的数据集,而不会耗尽内存资源。
在实现迭代器协议时,生成器自动处理了`__next__()`方法的细节,用户无需手动编写这个方法。通过`yield`语句,Python解释器知道如何暂停函数执行并保存其状态,以便下次调用`__next__()`时能够从上次暂停的地方继续执行。
#### 2.2.2 在并发编程中的应用实例
生成器在并发编程中的应用通常与其惰性求值和内存高效性有关。例如,在一个处理大量数据的场景中,我们可以使用生成器来逐个处理数据项,而不是一次性加载所有数据到内存中。这样可以减少内存占用,并且由于处理是按需发生的,这也为实现某些形式的并行处理提供了可能。
在多线程或异步编程中,可以利用生成器来暂停和恢复函数的执行,从而帮助管理协程。通过编写可以响应外部信号的生成器,我们可以创建协作式多任务处理,其中生成器在特定的点上显式地让出控制权,允许其他生成器运行。
下面是一个使用生成器在异步编程中实现协程的简单例子:
```python
import threading
def consumer():
while True:
item = yield
print(f'Consuming {item}')
def producer():
consumer_thread = threading.Thread(target=consumer)
consumer_thread.start()
for i in range(3):
consumer_thread.send(i)
producer()
```
在这个例子中,`consumer`函数是一个生成器,它从生产者接收项目并打印它们。`producer`函数创建了一个线程,该线程运行`consumer`生成器。`producer`函数通过`send`方法发送项目给`consumer`,后者依次打印它们。这个程序展示了如何在多线程环境下使用生成器进行异步任务。
### 2.3 性能考量与优化策略
#### 2.3.1 内存使用效率的优化
生成器的主要优势之一是内存效率。由于生成器逐个产生值,而不是一次性地计算和存储所有值,因此它们对于内存的使用更加高效。这对于处理大规模数据集或无限序列尤其重要,因为不需要将所有数据都保存在内存中。
优化内存使用的策略通常涉及使用生成器来避免不必要的数据复制或存储。在使用数据处理函数时,可以编写生成器来逐个处理数据项,而不是先将数据加载到内存中。这种方式对于I/O密集型任务特别有效,如文件读取、网络通信或数据库查询,其中数据传输和处理的速度由外部系统决定,而不是由CPU速度或内存大小决定。
此外,对于某些算法,可以通过生成器表达式替代列表推导式来减少内存占用。例如,计算一个大数列的平方时,使用生成器表达式可以按需计算平方,而不需要先创建整个数列的列表。
```python
squares = (x*x for x in range(1000000))
total = sum(squares)
```
在上面的代码中,我们创建了一个生成器`quares`,它按需产生平方值,避免了存储整个数列。
#### 2.3.2 生成器在大数据处理中的优势
在大数据处理场景中,生成器可以显著提高程序性能和资源利用率。传统上,处理大数据集通常需要大量的内存空间来存储整个数据集,这在处理非常大的数据集时可能是不可行的。使用生成器,可以实现数据的惰性加载和处理,这意味着数据处理可以在不将整个数据集加载到内存的情况下进行。
以下是使用生成器处理大数据的几个优势:
- **内存效率**:生成器只在需要时生成数据项,从而极大地减少了内存消耗。
- **避免数据过载**:在面对可能超出内存限制的大数据集时,生成器可以按需加载和处理数据,避免了程序崩溃的风险。
- **管道化处理**:在需要对数据进行多个步骤处理的场景中,生成器允许创建一个数据处理流水线,其中每个步骤都是惰性的,只有当前步骤处理完数据项之后,后续步骤才会开始处理。
例如,在处理CSV文件时,可以逐行读取文件并立即处理每一行,而不是将整个文件加载到内存中:
```python
import csv
def process_csv(filename):
with open(filename, 'r') as file:
reader = csv.reader(file)
for row in reader:
# 处理每一行数据
process(row)
def process(row):
# 对单行数据进行处理的具体逻辑
pass
process_csv('large_dataset.csv')
```
在这个例子中,`process_csv`函数逐行读取CSV文件,这样可以处理非常大的文件,而不会占用过多的内存。每读取一行,就立即调用`process`函数进行处理,之后可以立即丢弃读取的行,而无需存储整个文件的内容。
通过这种方式,使用生成器可以高效地处理大数据集,而不需要依赖大型硬件,使得大数据处理变得更加容易和可访问。
以上内容详细介绍了生成器的基本概念、创建方法以及进阶用法。生成器作为Python中一个重要的特性,不仅在理论上有其独特的地位,在实际应用中也展现出巨大的优势和潜力。理解并掌握生成器的内部机制和应用方式,对于提升程序性能和优化资源使用具有不可估量的价值。
# 3. 装饰器的设计与实现
在Python中,装饰器是一种特殊的函数,它可以让开发者在不修改原有函数定义的前提下,增加额外的功能。这种技术在许多开源项目和框架中广泛应用。本章将深入探讨装饰器的理论基础、高级技巧以及在实际框架和库中的应用案例。
## 3.1 装饰器的理论基础
### 3.1.1 闭包的理解与应用
闭包(Closure)是装饰器的基石。简单来说,闭包是一个函数,它能够访问并操作函数外部的变量。在Python中,闭包允许一个内部函数引用外部函数的参数和局部变量。
```python
def outer_function(msg):
message = msg
def inner_function():
print(message)
return inner_function # 返回一个闭包
hi_func = outer_function('Hi')
bye_func = outer_function('Bye')
hi_func() # 输出: Hi
bye_func() # 输出: Bye
```
在上述代码中,`inner_function` 就是闭包,它引用了外部函数`outer_function`的`message`变量。
### 3.1.2 装饰器的工作原理
装饰器是一个接受函数作为参数并返回一个新函数的函数。它通常用于在不改变原函数定义的情况下为函数增加新的功能。
```python
def my_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_hello():
print("Hello!")
decorated_hello = my_decorator(say_hello)
decorated_hello() # 输出: Something is happening before the function is called. Hello! Something is happening after the function is called.
```
在这个例子中,`my_decorator` 接收 `say_hello` 函数作为参数,返回了一个闭包 `wrapper`,该闭包在调用原始函数前后打印了额外的信息。
## 3.2 装饰器的高级技巧
### 3.2.1 装饰器的参数化
参数化装饰器允许装饰器接收参数,从而在不同场合下重用装饰器逻辑。这通常通过一个工厂函数来实现,该函数返回实际的装饰器。
```python
def decorator_with_args(number):
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Something is happening before the function is called.")
print("Decorator argument:", number)
result = func(*args, **kwargs)
print("Something is happening after the function is called.")
return result
return wrapper
return my_decorator
@decorator_with_args(42)
def say_hello(name):
print(f"Hello {name}")
say_hello("Python") # 输出: Something is happening before the function is called. Decorator argument: 42 Hello Python Something is happening after the function is called.
```
### 3.2.2 多重装饰器的叠加与顺序控制
在实际应用中,一个函数可能需要应用多个装饰器,Python解释器按照从里到外的顺序应用装饰器。
```python
def star(func):
def wrapper(*args, **kwargs):
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return wrapper
def percent(func):
def wrapper(*args, **kwargs):
print("%" * 30)
func(*args, **kwargs)
print("%" * 30)
return wrapper
@star
@percent
def printer(message):
print(message)
printer("Hello, World!") # 输出: ******%****** Hello, World! ******%******
```
在上述例子中,`printer` 函数先后应用了 `percent` 和 `star` 两个装饰器,调用时输出结果体现了装饰器的叠加顺序。
## 3.3 装饰器在框架与库中的应用
### 3.3.1 在Django等框架中的实际案例
在Web框架Django中,装饰器被广泛用于管理权限、处理缓存、维护会话状态等。例如,`login_required` 装饰器可以确保未经验证的用户无法访问受保护的视图。
```python
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
...
```
### 3.3.2 装饰器的设计模式与最佳实践
在设计装饰器时,应遵循开闭原则,即对扩展开放,对修改封闭。装饰器应该足够通用,以适应多种不同的函数和方法。同时,装饰器应该在增加额外功能的同时,尽量保持原有函数的执行效率和清晰性。
```python
# 一个更加通用的装饰器例子,支持接受额外的参数
def logged(level, name=None):
def decorator(func):
logname = name if name else func.__name__
def wrapper(*args, **kwargs):
print(f"Starting {logname} at level {level}")
result = func(*args, **kwargs)
print(f"Finishing {logname} at level {level}")
return result
return wrapper
return decorator
@logged(level="INFO")
def add(x, y):
return x + y
add(4, 5) # 输出: Starting add at level INFO Finishing add at level INFO
```
在此代码段中,`logged` 装饰器接受一个级别参数,并为被装饰的函数增加日志记录功能,使得装饰器更加灵活和可重用。
装饰器是Python高级特性中的重要一环,它极大地增强了代码的灵活性和可维护性。本章介绍了装饰器的基本概念、高级技巧和在框架中的应用案例,希望通过这些知识能够帮助读者更好地理解和使用装饰器这一强大的工具。
# 4. 上下文管理器的秘密
## 4.1 上下文管理器的原理与结构
### 4.1.1 `with`语句的工作机制
在Python中,`with`语句是上下文管理器的使用入口,它提供了一种方便的结构来处理资源的分配和释放。这种机制经常被用于文件操作、数据库连接以及线程同步等场景。本质上,`with`语句背后的实现基于一种特殊的协议——上下文管理协议。
当执行一个带有`with`的代码块时,`with`语句后面的对象会首先调用其`__enter__()`方法,紧接着执行`with`语句内的代码块。一旦代码块执行完毕,不管是因为正常执行完毕还是因为出现异常,`__exit__()`方法都会被调用。
下面是一个简化的`with`语句的工作流程:
1. 执行上下文管理器对象的`__enter__()`方法。
2. 将`__enter__()`方法返回的值赋给`with`语句后面的变量(如果有的话)。
3. 执行`with`语句块内的代码。
4. 无论`with`语句块内代码是否正常结束,都会执行上下文管理器对象的`__exit__()`方法,清理资源。
### 4.1.2 `__enter__`和`__exit__`方法解析
`__enter__`和`__exit__`是实现上下文管理协议的两个特殊方法,通常被称为上下文管理器的魔方法(magic methods)。它们分别对应于资源进入和退出的处理逻辑。
- `__enter__(self)`:当`with`块开始执行时,首先调用此方法。如果没有返回值,则默认返回`self`,可以返回其他值给`with`语句后面的变量。
- `__exit__(self, exc_type, exc_value, traceback)`:此方法在`with`块执行完毕后调用,无论执行过程中是否有异常发生。此方法的参数包含了异常的信息,如果`with`块中发生异常,它们会是非`None`的值,否则都是`None`。该方法的返回值决定了是否要忽略异常。如果返回`True`,则忽略异常;否则,异常会被重新抛出。
## 4.2 自定义上下文管理器
### 4.2.1 简单的自定义上下文管理器实现
自定义一个上下文管理器,最简单的方式是通过直接实现`__enter__`和`__exit__`方法的类来完成。
```python
class MyContextManager:
def __enter__(self):
print("Entering the context...")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Exiting the context...")
if exc_type is not None:
print(f"Exception handled: {exc_value}")
return False # Exception is not ignored
with MyContextManager():
print("Inside the with block.")
```
### 4.2.2 使用`contextlib`模块简化实现
Python的`contextlib`模块提供了一种更简洁的方式来创建上下文管理器,这通常被称为装饰器模式。使用`contextlib`中的`contextmanager`装饰器,可以将一个简单的生成器转换为上下文管理器。
```python
from contextlib import contextmanager
@contextmanager
def simple_context_manager():
print("Entering the context...")
yield
print("Exiting the context...")
with simple_context_manager():
print("Inside the with block.")
```
## 4.3 上下文管理器的实践应用
### 4.3.1 文件操作中的资源管理
使用`with`语句处理文件是最常见的上下文管理器应用场景。它确保了即使在发生异常的情况下,文件也会被正确关闭。
```python
with open('example.txt', 'w') as f:
f.write('Hello, Context Managers!')
```
### 4.3.2 线程和进程同步中的上下文应用
上下文管理器同样在多线程和多进程编程中用于资源同步。例如,使用`threading`模块中的`Lock`对象。
```python
from threading import Lock
lock = Lock()
with lock:
# Critical section
pass
```
在以上各节中,我们详细探讨了上下文管理器的工作机制和原理、如何自定义上下文管理器以及在不同场景中的应用。通过实际代码示例和逻辑分析,我们能够更深入地理解其背后的逻辑和使用方法,从而在软件开发中更加高效和安全地管理资源。
# 5. 三大特性综合应用与案例分析
## 5.1 实际项目中的综合应用
### 5.1.1 性能优化的实际案例
在软件开发过程中,性能优化是一个永恒的话题。对于Python这样的高级语言而言,高效地利用其高级特性可以在不牺牲代码可读性的同时,获得性能上的提升。让我们通过一个实际案例来了解这一过程。
假设我们正在开发一个Web应用,需要处理大量的数据查询请求。最开始,我们可能会使用简单的for循环来遍历数据库记录,并进行处理。但是随着数据量的增加,这种做法会导致性能瓶颈。这时,我们可以利用生成器的惰性求值特性,来优化内存使用和处理速度。
```python
def data_query(db_cursor):
for record in db_cursor:
yield record
# 使用生成器代替列表存储所有数据
for record in data_query(db_cursor):
process(record)
```
在这个例子中,`data_query`函数返回一个生成器,而不是将所有记录加载到内存中。`process`函数可以是对每条记录进行处理的函数。由于生成器一次只产生一个记录,这大大减少了内存的使用,并且允许处理速度与数据库读取速度匹配,提高了整体性能。
### 5.1.2 构建可复用代码库的策略
代码复用是提高开发效率和维护性的重要手段。Python的装饰器和上下文管理器都可以用来构建可复用的代码库。通过定义通用的装饰器,可以轻松地给函数添加日志、验证或其他通用功能。
```python
import functools
def logged(func):
"""一个装饰器,用于记录函数的调用信息"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@logged
def add(x, y):
"""返回两个数的和"""
return x + y
```
在这个例子中,`logged`装饰器可以用于任何函数,记录其调用和返回值,而无需修改函数本身的代码。
同时,上下文管理器在资源管理方面提供了极大的便利,特别是对于需要频繁打开和关闭资源的情况。例如,可以创建一个通用的上下文管理器,用于自动管理文件的打开和关闭。
```python
from contextlib import contextmanager
@contextmanager
def managed_file(name):
try:
f = open(name, "w")
yield f
finally:
f.close()
```
使用这个上下文管理器,可以轻松地在文件操作中自动管理资源。
## 5.2 三大特性的优势与局限
### 5.2.1 在不同场景下选择合适的技术
Python的生成器、装饰器和上下文管理器各有其优势和适用场景。生成器最适合用于大规模数据的惰性处理;装饰器适合于添加通用功能而不改变原有函数逻辑;上下文管理器则适用于资源管理,尤其是那些需要明确打开和关闭的场景。
在选择使用这些特性时,重要的是了解其内在的工作机制和适用条件。例如,在处理大型文件或数据集时,使用生成器可以避免内存溢出;而在构建通用功能层,比如日志记录、权限检查等,使用装饰器则可以保持代码的整洁和清晰;上下文管理器则适用于确保代码块执行完毕后资源的正确释放。
### 5.2.2 避免过度设计与复杂化的建议
虽然Python的高级特性非常强大,但在实际应用中也需要小心避免过度设计。过度设计不仅会增加代码的复杂性,还可能引入难以发现的错误。因此,需要在代码的简洁性、可读性和功能的丰富性之间找到平衡点。
例如,装饰器虽然可以让代码更加通用和简洁,但是过度使用或滥用装饰器会使代码难以追踪和维护。同样,生成器虽然可以优化内存使用,但在某些情况下,简单直观的循环或列表推导可能更加合适。
最佳实践是,首先在代码中寻找可能的重复模式,然后评估是否可以通过装饰器、生成器或上下文管理器等高级特性来简化和优化。在做决定时,保持代码的清晰和简单应该是首要考虑的因素。
# 6. ```
# 第六章:高级编程思维与最佳实践
## 6.1 编程思维的培养与应用
### 6.1.1 理解问题本质的重要性
编写代码之前,理解问题的本质是至关重要的一步。这不仅包括了解问题的具体要求和边界条件,还涉及到对问题背景、相关业务逻辑的深入洞察。只有对问题有了深刻的理解,才能编写出既有效率又易于维护的代码。
一个常见的例子是处理日志文件。如果只看到了处理日志的需求,可能会直接编写一个解析日志的函数。但是深入分析后,可能会发现日志文件有多种类型,每种类型的日志需要特定的解析方法。这就需要设计一个更灵活的日志处理框架来适应不同的需求。
### 6.1.2 高级编程模式的探索与实践
高级编程思维不仅仅是应用高级特性,还包括对编程模式的理解和应用。例如,设计模式提供了一套被验证过的解决常见问题的模板,它们可以帮助开发者提高代码的复用性、可读性和维护性。
以策略模式为例,当算法的使用场景可能会变化时,我们可以将算法的定义和使用分离。这样,算法的变更不会影响到使用算法的代码。策略模式特别适合于一系列的算法可以相互替换的场景。
## 6.2 代码的最佳实践与维护
### 6.2.1 编写可读性强与可维护的代码
好的代码应该如诗般简洁明了,易于其他开发者阅读和理解。为了达到这个目标,遵循一些代码规范和最佳实践是必要的,比如Pylint和PEP 8风格指南。
此外,代码的注释也是不可或缺的。注释应该解释代码为什么这么做,而不是怎么做的(后者应该通过代码本身清晰表达)。良好的代码结构和命名规则也是提高代码可读性的关键。
### 6.2.2 单元测试与持续集成的推广
单元测试和持续集成是保证代码质量的重要手段。单元测试可以确保每个独立的代码片段按照预期工作,而持续集成能够在代码库发生变化时自动化运行测试,确保新的变更没有破坏现有功能。
在Python社区,unittest和pytest是流行的单元测试框架。对于持续集成,Jenkins、Travis CI、GitLab CI都是不错的选择。通过这些工具,开发者可以更专注于开发,减少因集成导致的错误。
## 6.3 洞悉未来:Python的未来发展方向
### 6.3.1 新特性的快速迭代与应用前景
Python语言一直在快速迭代中,不断引入新的特性和改进。例如,Python 3.8引入了赋值表达式,Python 3.9则改进了字典的性能和类型提示的表达。这些新特性旨在使Python更加易用和强大。
了解和应用这些新特性可以帮助开发者编写更高效的代码。对于大型项目,适时地迁移到新版本Python可以提高开发效率和代码质量。
### 6.3.2 在新兴技术领域中的Python表现
随着技术的发展,Python在人工智能、机器学习、数据分析、自动化测试、Web开发等领域表现抢眼。它的简洁语法和强大的库生态系统使其成为这些领域的首选语言。
在人工智能领域,TensorFlow和PyTorch等深度学习框架都支持Python。在Web开发方面,Django和Flask等框架让Python成为构建后端服务的热门选择。Python的广泛流行和多功能性使其在未来几年内仍将是IT行业的宠儿。
```
0
0