# Python 进阶之道

Python 和 JavaScript 在笔者看来是很相似的语言，本文归纳了 Python 的各种 tricks。

## 函数

### 匿名函数

函数的简化写法，配合 map、filter、reduce 等高阶函数实现函数式编程

```python
# def foo(parameters):
#     return expression
foo = lambda parameters: expression
```

#### map - 映射

```python
numbers = [1, 2, 3, 4, 5]
list(map(lambda e: e ** 2, numbers))
# [1, 4, 9, 16, 25]
```

#### filter - 过滤

```python
values = [None, 0, '', True, 'alphardex', 666]
list(filter(lambda e:e, values))
# [True, "alphardex", 666]
```

#### reduce - 归并

```python
from functools import reduce
numbers = [1, 2, 3, 4, 5]
reduce(lambda acc, cur: acc + cur, numbers)
# 15
```

### 星号和双星号

#### 数据容器的合并

```python
l1 = ['kaguya', 'miyuki']
l2 = ['chika', 'ishigami']
[*l1, *l2]
# ['kaguya', 'miyuki', 'chika', 'ishigami']
d1 = {'name': 'rimuru'}
d2 = {'kind': 'slime'}
{**d1, **d2}
# {'name': 'rimuru', 'kind': 'slime'}
```

#### 函数参数的打包与解包

```python
# 打包
def foo(*args):
    print(args)
foo(1, 2)
# (1, 2)

def bar(**kwargs):
    print(kwargs)
bar(name='hayasaka', job='maid')
# {'name': 'hayasaka', 'job': 'maid'}

# 解包
t = (10, 3)
quotient, remainder = divmod(*t)
quotient
# 商：3
remainder
# 余：1
```

## 数据容器

### 列表

#### 迭代

```python
li = ['umaru', 'ebina', 'tachibana']
for e in li:
    print(li)
# umaru ebina tachibana
```

#### 同时迭代元素与其索引

用 enumerate 即可

```python
li = ['umaru', 'ebina', 'tachibana']
print([f'{i+1}. {elem}' for i, elem in enumerate(li)])
# ['1. umaru', '2. ebina', '3. tachibana']
```

#### 同时迭代 2 个以上的可迭代对象

用 zip 即可

```python
subjects = ('nino', 'miku', 'itsuki')
predicates = ('saikou', 'ore no yome', 'is sky')
print([f'{s} {p}' for s, p in zip(subjects, predicates)])
# ['nino saikou', 'miku ore no yome', 'itsuki is sky']
```

#### 测试是否整体/部分满足条件

all 测试所有元素是否都满足于某条件，any 则是测试部分元素是否满足于某条件

```python
all(e<20 for e in [1, 2, 3, 4, 5])
# True
any(e%2==0 for e in [1, 3, 4, 5])
# True
```

#### 解包

最典型的例子就是 2 数交换

```python
a, b = b, a
# 等价于 a, b = (b, a)
```

用星号运算符解包可以获取剩余的元素

```python
first, *rest = [1, 2, 3, 4]
first
# 1
rest
# [2, 3, 4]
```

用下划线可以忽略某个变量

```python
filename, _ = 'eroge.exe'.split('.')
filename
# 'eroge'
```

### 字典

#### 迭代

```python
d = {'name': 'sekiro', 'hobby': 'blacksmithing', 'tendency': 'death'}
[key for key in d.keys()]
# ['name', 'hobby', 'tendency']
[value for value in d.values()]
# ['sekiro', 'blacksmithing', 'death']
[f'{key}: {value}' for key, value in d.items()]
# ['name: sekiro', 'hobby: blacksmithing', 'tendency: death']
```

#### 排序

```python
data = [{'rank': 2, 'author': 'alphardex'}, {'rank': 1, 'author': 'alphardesu'}]
data_by_rank_desc = sorted(data, key=lambda d: d['rank'], reverse=True)
# [{'rank': 2, 'author': 'alphardex'}, {'rank': 1, 'author': 'alphardesu'}]
```

#### 缺失键处理

get 返回键值，如果键不在字典中，将会返回一个默认值

```python
d = {'name': 'okabe rintaro', 'motto': 'elpsycongroo'}
d.get('job', 'mad scientist')
# mad scientist
```

setdefault 返回键值，如果键不在字典中，将会添加它并设置一个默认值

```python
d = {'name': 'okabe rintaro', 'motto': 'elpsycongroo'}
# if 'job' not in d:
#     d['job'] = 'mad scientist'
d.setdefault('job', 'mad scientist')
# mad scientist
d
# {'name': 'okabe rintaro', 'motto': 'elpsycongroo', 'job': 'mad scientist'}
```

## 语言专属特性

### 推导式

推导式是一种快速构建可迭代对象的方法，因此凡是可迭代的对象都支持推导式

#### 列表推导式

获取 0-10 内的所有偶数

```python
even = [i for i in range(10) if not i % 2]
even
# [0, 2, 4, 6, 8]
```

#### 字典推导式

将装满元组的列表转换为字典

```python
SEIREI = [(0, 'takamiya mio'), (1, 'tobiichi origami'), (2, 'honjou nia'), (3, 'tokisaki kurumi'), (4, 'yoshino'), (5, 'itsuka kotori'), (6, 'hoshimiya mukuro'), (7, 'natsumi'), (8, 'yamai'), (9, 'izayoi miku'), (10, 'yatogami tohka')]
seirei_code = {seirei: code for code, seirei in SEIREI}
seirei_code
# {'takamiya mio': 0, 'tobiichi origami': 1, 'honjou nia': 2, 'tokisaki kurumi': 3, 'yoshino': 4, 'itsuka kotori': 5, 'hoshimiya mukuro': 6, 'natsumi': 7, 'yamai': 8, 'izayoi miku': 9, 'yatogami tohka': 10}
{code: seirei.upper() for seirei, code in seirei_code.items() if code > 6}
# {7: 'NATSUMI', 8: 'YAMAI', 9: 'IZAYOI MIKU', 10: 'YATOGAMI TOUKA'}
```

#### 集合推导式

求所有数字的平方并去除重复元素

```python
{x ** 2 for x in [1, 2, 2, 3, 3]}
# {1, 4, 9}
```

#### 生成器表达式

求 0-10 内的所有偶数的和

```python
even_sum_under_10 = sum(i for i in range(11) if not i % 2)
even_sum_under_10
# 30
```

### 装饰器

装饰器是一个可调用的对象，顾名思义它能够装饰在某个可调用的对象上，给它增加额外的功能

常用于缓存、权限校验、日志记录、性能测试、事务处理等场景

以下实现了一个简单的日志装饰器，能打印出函数的执行时间、函数名、函数参数和执行结果

```python
import time
from functools import wraps

def clock(func):
    @wraps(func) # 防止被装饰函数的属性被wrapper覆盖
    def wrapper(*args, **kwargs):
        t0 = time.perf_counter()
        result = func(*args, **kwargs) # 由于闭包，wrapper函数包含了自由变量func
        elapsed = time.perf_counter() - t0
        name = func.__name__
        args = ', '.join(repr(arg) for arg in args)
        kwargs = ', '.join(f'{k}={w}' for k, w in sorted(kwargs.items()))
        all_args_str = ', '.join(astr for astr in [args_str, kwargs_str] if astr)
        print(f'[{elapsed:.8f}s] {name}({all_args_str}) -> {result}')
        return result
    return wrapper # 返回内部函数，取代被装饰的函数

@clock
def factorial(n: int) -> int:
    return 1 if n < 2 else n * factorial(n-1)

factorial(5)
# [0.00000044s] factorial(1) -> 1
# [0.00011111s] factorial(2) -> 2
# [0.00022622s] factorial(3) -> 6
# [0.00030844s] factorial(4) -> 24
# [0.00042222s] factorial(5) -> 120
# 120
```

如果想让装饰器能接受参数，那就要再嵌套一层

```python
import time
from functools import wraps

DEFAULT_FMT = '[{elapsed:.8f}s] {name}({all_args_str}) -> {result}'

def clock(fmt=DEFAULT_FMT):
    def decorate(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            t0 = time.perf_counter()
            result = func(*args, **kwargs)
            elapsed = time.perf_counter() - t0
            name = func.__name__
            args_str = ', '.join(repr(arg) for arg in args)
            kwargs_str = ', '.join(f'{k}={w}' for k, w in sorted(kwargs.items()))
            all_args_str = ', '.join(astr for astr in [args_str, kwargs_str] if astr)
            print(fmt.format(**locals()))
            return result
        return wrapper
    return decorate

@clock()
def factorial_default_fmt(n: int) -> int:
    return 1 if n < 2 else n * factorial_default_fmt(n-1)

@clock('{name}: {elapsed}s')
def factorial_customed_fmt(n: int) -> int:
    return 1 if n < 2 else n * factorial_customed_fmt(n-1)

factorial_default_fmt(3)
# [0.00000044s] factorial_default_fmt(1) -> 1
# [0.00009600s] factorial_default_fmt(2) -> 2
# [0.00018133s] factorial_default_fmt(3) -> 6
# 6
factorial_customed_fmt(3)
# factorial_customed_fmt: 4.444450496521313e-07s
# factorial_customed_fmt: 9.733346314533264e-05s
# factorial_customed_fmt: 0.0001831113553407704s
# 6
```

在 django 中，可以通过装饰器对函数视图进行功能增强（比如@login\_required 进行登录的权限校验，@cache\_page 进行视图的缓存等）

### 上下文管理器

用于资源的获取与释放，以代替 try-except 语句

常用于文件 IO，锁的获取与释放，数据库的连接与断开等

```python
# try:
#     f = open(input_path)
#     data = f.read()
# finally:
#     f.close()
with open(input_path) as f:
    data = f.read()
```

其实在 pathlib 里已经给我们封装好了文件 IO 方法

```python
# with open('file') as i:
#     data = i.read()
from pathlib import Path
data = Path('file').read_text()
```

至于上下文管理器的实现，可以用@contextmanager

```python
from contextlib import contextmanager

@contextmanager
def open_write(filename):
    try:
        f = open(filename, 'w')
        yield f
    finally:
        f.close()

with open_write('onegai.txt') as f:
    f.write('Dagakotowaru!')
```

### 多重继承

在 django 中经常要处理类的多重继承的问题，这时就要用到 super 函数

如果单单认为 super 仅仅是“调用父类的方法”，那就错了

在继承单个类的情况下，可以认为 super 是调用父类的方法（ES6 里面亦是如此）

但多重继承就不一样了，因为方法名可能会有冲突，所以 super 就不能单指父类了

**在 Python 中，super 指的是 MRO 中的下一个类**，用来解决多重继承时父类的查找问题

MRO 是啥？Method Resolution Order（方法解析顺序）

看完下面的例子，就会理解了

```python
class A:
    def __init__(self):
        print('A')

class B(A):
    def __init__(self):
        print('enter B')
        super().__init__()
        print('leave B')

class C(A):
    def __init__(self):
        print('enter C')
        super().__init__()
        print('leave C')

class D(B, C):
    pass

d = D()
# enter B
# enter C
# A
# leave C
# leave B
print(d.__class__.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
```

首先，因为 D 继承了 B 类，所以调用 B 类的\_\_init\_\_，打印了`enter B`

打印`enter B`后的 super 寻找 MRO 中的 B 的下一个类，也就是 C 类，并调用其\_\_init\_\_，打印`enter C`

打印`enter C`后的 super 寻找 MRO 中的 C 的下一个类，也就是 A 类，并调用其\_\_init\_\_，打印`A`

打印`A`后回到 C 的\_\_init\_\_，打印`leave C`

打印`leave C`后回到 B 的\_\_init\_\_，打印`leave B`

### 特殊方法

在 django 中，定义 model 的时候，希望 admin 能显示 model 的某个字段而不是 XXX Object，那么就要定义好\_\_str\_\_

每当你使用一些内置函数时，都是在调用一些特殊方法，例如 len()调用了\_\_len\_\_(), str()调用\_\_str\_\_()等

以下实现一个 2d 数学向量类，里面有多个特殊方法

```python
from math import hypot

class Vector2d:

    # 限制允许绑定的属性
    __slots__ = ('__x', '__y')

    # 实例创建
    def __init__(self, x, y):
        self.__x = float(x)
        self.__y = float(y)

    # 前双下划线是私有属性，property装饰是只读属性
    @property
    def x(self):
        return self.__x

    @property
    def y(self):
        return self.__y

    # 可迭代对象
    def __iter__(self):
        yield from (self.x, self.y)

    # 字符串表示形式
    def __repr__(self) -> str:
        return f'{type(self).__name__}({self.x}, {self.y})'

    # 数值转换 - 绝对值
    def __abs__(self) -> float:
        return hypot(self.x, self.y)

    # 数值转换 - 布尔值
    def __bool__(self) -> bool:
        return bool(abs(self))

    # 算术运算符 - 加
    def __add__(self, other):
        x = self.x + other.x
        y = self.y + other.y
        return Vector2d(x, y)

    # 算术运算符 - 乘
    def __mul__(self, scalar: float):
        return Vector2d(self.x * scalar, self.y * scalar)

    # 比较运算符 - 相等
    def __eq__(self, other):
        return tuple(self) == tuple(other)

    # 可散列
    def __hash__(self):
        return hash(self.x) ^ hash(self.y)

v = Vector2d(3, 4)

# __slots__限制了允许绑定的属性，只能是x或y
v.z = 1
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AttributeError: 'Vector2d' object has no attribute 'z'

# 由于x属性只读，因此无法再次赋值
v.x = 1
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# AttributeError: can't set attribute

# iter(v) => v.__iter__()
x, y = v
# x为3, y为4

# repr(v) => v.__repr__()
v
# Vector2d(3, 4)

# abs(v) => v.__abs__()
abs(v)
# 5.0

# bool(v) => v.__bool__()
bool(v)
# True

# v1 + v2  => v1.__add__(v2)
v1 = Vector2d(1, 2)
v2 = Vector2d(3, 4)
v1 + v2
# Vector2d(4, 6)

# v * 3  => v.__mul__(3)
v * 3
# Vector2d(9, 12)

# v1 == v2 => v1.__eq__(v2)
v1 = Vector2d(1, 2)
v2 = Vector2d(1, 2)
v1 == v2
# True

# hash(v) => v.__hash__()
hash(v)
# 7
v1 = Vector2d(1, 2)
v2 = Vector2d(3, 4)
set([v1, v2])
# {Vector2d(1.0, 2.0), Vector2d(3.0, 4.0)}
```

如果把 Vector 改造为多维向量呢？关键就是要实现序列协议（\_\_len\_\_和\_\_getitem\_\_）

协议：本质上是**鸭子类型**语言使用的非正式接口

不仅如此，还要实现多分量的获取以及散列化

```python
from array import array
import reprlib
import math
import numbers
import string
from functools import reduce
from operator import xor
from itertools import zip_longest
import numbers
from fractions import Fraction as F

class Vector:
    typecode = 'd'
    shortcut_names = 'xyzt'

    def __init__(self, components):
        self._components = array(self.typecode, components)

    def __iter__(self):
        return iter(self._components)

    def __repr__(self):
        components = reprlib.repr(self._components)
        components = components[components.find('['):-1]
        return f'{type(self).__name__}({components})'

    def __str__(self):
        return str(tuple(self))

    def __eq__(self, other):
        return tuple(self) == tuple(other)

    def __bool__(self):
        return bool(abs(self))

    # 序列协议 - 获取长度
    def __len__(self):
        return len(self._components)

    # 序列协议 - 索引取值
    def __getitem__(self, index):
        cls = type(self)  # Vector
        if isinstance(index, slice):  # 索引是slice对象，则返回Vector实例
            return cls(self._components[index])
        elif isinstance(index, numbers.Integral):  # 索引是整数类型，则返回_components中对应的数字
            return self._components[index]
        else:
            raise TypeError(f'{cls.__name__} indices must be integers.')

    # 属性访问，获取分量的值
    def __getattr__(self, name):
        cls = type(self)
        if len(name) == 1:
            pos = cls.shortcut_names.find(name)
            if 0 <= pos < len(self._components):
                return self._components[pos]
        raise AttributeError(f'{cls.__name__} has no attribute {name}')

    # 属性设置，给分量设值时会抛出异常，使向量是不可变的
    def __setattr__(self, name, value):
        cls = type(self)
        if len(name) == 1:
            if name in string.ascii_lowercase:
                raise AttributeError(f"can't set attribute 'a' to 'z' in {cls.__name__}")
        super().__setattr__(name, value)

    # 比较所有分量，都相等才算两向量相等
    def __eq__(self, other):
        return len(self) == len(other) and all(a == b for a, b in zip(self, other))

    # 散列化
    def __hash__(self):
        hashes = map(hash, self._components)
        return reduce(xor, hashes, 0)

    # 绝对值
    def __abs__(self):
        return math.sqrt(sum(x ** 2 for x in self))

    # 取正
    def __pos__(self):
        return Vector(self)

    # 取负
    def __neg__(self):
        return Vector(-x for x in self)

    # 加 (减法__sub__的实现与之类似，略)
    def __add__(self, other):
        try:
            return Vector(a + b for a, b in zip_longest(self, other, fillvalue=0.0))
        except TypeError:
            return NotImplemented

    # 反向加（a+b中，如果a没有__add__或返回NotImplemented，则检查b是否有__radd__，有则调用之）
    def __radd__(self, other):
        return self + other

    # 乘 (除法__truediv__的实现与之类似，略)
    def __mul__(self, scalar):
        return Vector(n * scalar for n in self) if isinstance(scalar, numbers.Real) else NotImplemented

    # 反向乘
    def __rmul__(self, scalar):
        return self * scalar

    # 中缀运算符@ - 点积
    def __matmul__(self, other):
        try:
            return sum(a * b for a, b in zip(self, other))
        except TypeError:
            return NotImplemented

    # 反向中缀运算符@
    def __rmatmul__(self, other):
        return self @ other

v = Vector(range(7))
v
# Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])

v[1:3]
# Vector([1.0, 2.0])

v[-1]
# 6.0

v[1,3]
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 39, in __getitem__
# TypeError: Vector indices must be integers.

v.x, v.y, v.z
# (0.0, 1.0, 2.0)

v.x = 1
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "<stdin>", line 62, in __setattr__
# AttributeError: can't set attribute 'a' to 'z' in Vector

v1 = Vector((3, 4, 5))
v2 = Vector((6, 7))

v1 == v2
# False

set([v1, v2])
# {Vector([6.0, 7.0]), Vector([3.0, 4.0, 5.0])}

abs(v)
# 9.539392014169456

+v
# Vector([0.0, 1.0, 2.0, 3.0, 4.0, ...])

-v
# Vector([-0.0, -1.0, -2.0, -3.0, -4.0, ...])

v1 + v2
# Vector([9.0, 11.0, 5.0])

v * 3
# Vector([0.0, 3.0, 6.0, 9.0, 12.0, ...])

v * F(1, 2)
# Vector([0.0, 0.5, 1.0, 1.5, 2.0, ...])

v1 @ v2
# 46.0
```

想了解所有的特殊方法可查阅[官方文档](https://docs.python.org/3/reference/datamodel.html#special-method-names),以下列举些常用的：

```python
字符串表示形式：__str__, __repr__
数值转换：__abs__, __bool__, __int__, __float__, __hash__
集合模拟：__len__, __getitem__, __setitem__, __delitem__, __contains__
迭代枚举：__iter__, __reversed__, __next__
可调用模拟：__call__
实例创建与销毁：__init__, __del__
属性访问：__getattr__, __setattr__
运算符相关：__add__, __radd__, __mul__, __rmul__, __matmul__, __rmatmul__, ...
```

### 类方法和静态方法

@classmethod 是类方法，它定义操作类的方法，也就是说会将类绑定给方法，而不是实例

@staticmethod 是静态方法，啥都不绑定，一般用来给类绑定各种工具方法（不涉及对实例和类的操作）

在 django 中，我们经常要在视图函数中对模型类进行各种查询

然而，很多查询都是重复的代码，根据 DRY 原则，它们都是可以被封装的

那么，如果我们要给模型类封装一些查询操作，就要用到@classmethod

以下是 Post 类，里面定义了 latest\_posts 方法用来获取最新的几个 Post

这样在视图函数中，就能直接调用该方法进行查询，节省了不少代码

```python
class Post(models.Model):
    STATUS_NORMAL = 1
    STATUS_DELETE = 0
    STATUS_DRAFT = 2
    STATUS_ITEMS = (
        (STATUS_NORMAL, '正常'),
        (STATUS_DELETE, '删除'),
        (STATUS_DRAFT, '草稿'),
    )
    ...
    status = models.PositiveIntegerField(_("状态"), choices=STATUS_ITEMS, default=STATUS_NORMAL)
    created_time = models.DateTimeField(_("创建时间"), auto_now_add=True)
    ...

    @classmethod
    def latest_posts(cls, limit=None):
        queryset = cls.objects.filter(status=cls.STATUS_NORMAL).order_by('-created_time')
        if limit:
            queryset = queryset[:limit]
        return queryset
```

### 描述符

实现了\_\_set\_\_或\_\_get\_\_协议的类就是描述符

set 和 get 代表存和取，因此描述符是一种对多个类属性运用相同存取逻辑的一种方式

例如 django 的 ORM 中的字段类型是描述符，用来把数据库记录中的字段数据与 Python 对象的属性对应起来

以下实现一个简单的描述符类，用来在读写属性时验证属性的正确性

```python
class Validator:
    def __init__(self, storage_name):
        self.storage_name = storage_name

    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise ValueError('Value must be an integer')
        if value > 200:
            raise ValueError('Value must be under 200')

class Person:
    age = Validator('age')

    def __init__(self, age):
        self.age = age

person = Person(age=100)
person.age = 'young'
# Traceback (most recent call last):
# ValueError: Value must be an integer
person.age = 201
# Traceback (most recent call last):
# ValueError: Value must be under 200
```

### 元类

进入元类这个概念之前，我们先回顾一下 type()这个函数，不，其实它是个类

通过 type()，我们可以获取一个对象所属的类，但通过 help 函数，发现 type()居然也可以用来创建类！

```python
type(name, bases, dict) -> a new type
```

name 是新类的名称，bases 是继承的子类，dict 则是新类的属性名与其对应值的字典

```python
class A:
    a = 1
    def foo(self):
        return self.a * 2

# 以上类的创建等价于
A = type('A', (object, ), {'a': 1, 'foo': lambda self: self.a * 2})
```

那么什么是元类呢？

平时我们用类来创建对象，但一切类都继承了对象，说白了类也是对象，而元类就是用来创建类对象的类

说白了，元类就是制造类的工厂

```python
'alphardex'.__class__
# <class 'str'>
'alphardex'.__class__.__class__
# <class 'type'>
```

通过以上的例子我们知道 type 就是用来创造一切类的元类，它是 Python 内置的元类

既然有内置的元类，也意味着你也可以自定义元类

以下实现一个元类，用来把类的所有非私有属性自动转换为大写（不已\_开头的属性都是非私有的）

思路很简单：把属性和对应的值字典(attrs)里的非私有属性键改为大写(upper)就行了

```python
class UpperAttrMeta(type):
    def __new__(cls, name, bases, attrs):
        """
        __init__方法用来初始化对象并传入参数
        而__new__方法专门用来创建对象（显然这里我们要创建一个类对象并定制它）
        """
        upper_attrs = {k.upper() if not k.startswith('_') else k: v for k, v in attrs.items()}
        return super().__new__(cls, name, bases, upper_attrs)

class Foo(metaclass=UpperAttrMeta):
    name = 'alphardex'
    __love = 'unknown'

f = Foo()
f.NAME
# 'alphardex'
f._Foo__love
# 'unknown'
```

元类的最经典的用途就是 ORM 的实现，以 django 的 ORM 为例

```python
class Person(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()

p = Person(name='alphardex', age='24')
p.age
# 24
```

如果你访问一个模型实例的属性（例如这里的 age），你并不会得到什么 IntegerField()，而是得到了 24 这个数字，这就是元类的作用

元类平时很少用到，如果要动态修改类的属性，可以用猴子补丁（直接修改类方法）或者类装饰器

当然，这并不代表元类没什么用，想用到它的时候自然会用到的


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://alphardex.gitbook.io/coder-operation-record/hou-duan/python.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
