# 重读 Python 官方文档

\[TOC]

## 1. Lexical analysis

### 1.1 编码方式注释

```python
# -*- encoding:<encodeing-name> -*-
# vim:fileencoding=<encoding-name>
```

由多个物理行连接而成的逻辑行需要使用`\`链接。

### 1.2 保留的类的标识符

* `_*`:不可以使用`from module import *`导入
* `__*`:类的 private 成员
* `__*__`:系统定义的标识符，由解释器定义以及实现

### 1.3 字符串字面值

python3 使用的字符串使用Unicode编码，对于ASCII编码转换为 byte 处理

#### 1.3.1 string

`stringliteral ::= [stringprefix](shortstring | longstring)`

可以选择添加相应的字符串前缀，可选的字符串前缀，可选的值是："r" | "u" | "R" | "U" | "f" | "F" | "fr" | "Fr" | "fR" | "FR" | "rf" | "rF" | "Rf" | "RF"

* r 或 R：定义一个原生字符串（raw strings），不使用  转义字符串，在其中的字符都当做普通字符串。
* u 或 U：定义一个使用Unicode编码的字符串
* f 或 F：定义一个格式化的字符串
* 其中 `r | R` 可以和 `f | F` 联合使用

**之前的format（）函数可以在一些场景下使用f''替代**

#### 1.3.2 byte

可以选择的 byte 前缀包括： "b" | "B" | "br" | "Br" | "bR" | "BR" | "rb" | "rB" | "Rb" | "RB" | “f”

代表含义与string相似，转换为 byte 必须加 `'b'`

#### 1.3.3 格式化字符串

```python
>>> name = "Fred"
>>> f"He said his name is {name!r}."
"He said his name is 'Fred'."
>>> f"He said his name is {repr(name)}."  # repr() is equivalent to !r
"He said his name is 'Fred'."
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'
>>> today = datetime(year=2017, month=1, day=27)
>>> f"{today:%B %d, %Y}"  # using date format specifier
'January 27, 2017'
>>> number = 1024
>>> f"{number:#0x}"  # using integer format specifier
'0x400'
```

### 1.4 数字字面值

#### 1.4.1 不同进制

* binary ::= 0 (b|B)\[\_] 0-1
* oct ::= 0 (O|o) \[\_] 0-7
* hex ::= 0(x|X) \[\_] 0-f
* 指数： num (e|E) num
* 虚数： float|digit (j|J)

可以在数字字面值中使用`_`作为分隔符，便于阅读，不可以位于数字末尾。可以位于`0b_10001_0`

## 2. 数据类型

### 2.1 基本数据类型

**一下数据类型可能包含一些特定参数，但是在以后的版本中可能变化**

* None：内置空值
* NotImplemented： 内置未实现类型
* Ellipsis： 内置省略类型，可以通过`...`访问可能在数字计算中使用到，例如获取一个矩阵的一个列：`array[..., 0]`
* numbers.Integer
  * int
  * bool: False => 0, others => True (Not equal)
* numbers.Real float
* numbers.Complex 具有两个只读的属性:
  * real
  * imag
* Sequences
  * 不可变序列

    > * String: 可表示范围 U+0000 - U+10FFFF，python 没有 char 类型，最基本的字符类型使用长度为1的string表示，`ord()`方法将一个码点转换为string,`chr()`将一个String转换为 int
    > * Tuple
    > * Bytes
  * 可变序列

    > * Lists
    > * bytes array
* 集合类型：
  * sets： `set()`方法构建，`add()`添加
  * 不可变集合： frozenset()&#x20;
* 映射类型：

  mapping

## 3. 包管理

python 定义了两种类型的包管理方式基本的包管理方式，以及命名域的包管理。基本的包管理策略，在每一个 package 下放置一个`__init__.py`的文件。导入常规包时，将隐式执行此**init**.py文件，并且它定义的对象将绑定到包命名空间中的名称。 **init**.py文件可以包含与任何其他模块可以包含的相同的Python代码，并且Python将在导入模块时向模块添加一些其他属性。

```
parent/
    __init__.py
    one/
        __init__.py
    two/
        __init__.py
    three/
        __init__.py
```

命名空间包是各个部分的组合，其中每个部分为父包提供子包。部分可以驻留在文件系统上的不同位置。也可以在zip文件，网络或Python导入期间搜索的任何其他位置找到部分。命名空间包可能或可能不直接对应于文件系统上的对象;它们可能是没有具体表示的虚拟模块。可以使用添加`sys.path`变量获取命名域,不需要使用`__init__.py`

```python
>>> import sys
>>> sys.path += ['Lib/test/namespace_pkgs/project1', 'Lib/test/namespace_pkgs/project2']
>>> import parent.child.one
>>> parent.__path__
_NamespacePath(['Lib/test/namespace_pkgs/project1/parent', 'Lib/test/namespace_pkgs/project2/parent'])
>>> parent.child.__path__
_NamespacePath(['Lib/test/namespace_pkgs/project1/parent/child', 'Lib/test/namespace_pkgs/project2/parent/child'])
>>> import parent.child.two
>>>
```

对应的目录结构：

```
Lib/test/namespace_pkgs
    project1
        parent
            child
                one.py
    project2
        parent
            child
                two.py
```

## 4. Generator

对于 python的可遍历类型，需要为每一个类实现`__iter__()`, `__next__()`方法，很耗时费力，可以使用生成器简化操作。可以在一个函数或者表达式中使用`yield`关键字构建一个generator，也可以使用generator 产生式:

```python
# Intialize the list
my_list = [1, 3, 6, 10]

a = (x**2 for x in my_list)
# Output: 1
print(next(a))

# Output: 9
print(next(a))

# Output: 36
print(next(a))

# Output: 100
print(next(a))

# Output: StopIteration
next(a)
sum(x**2 for x in my_list)
```

## 5.异步

### 5.1 协程

由于GIL的存在，导致python的多线程性能甚至比单线程还慢。

> GIL: 全局解释器锁（英语：Global Interpreter Lock，缩写GIL），是计算机程序设计语言解释器用于同步线程的一种机制，它使得任何时刻仅有一个线程在执行。\[1]即便在多核心处理器上，使用 GIL 的解释器也只允许同一时间执行一个线程。

所以出现了协程的概念，与Golang中的Goroutines不同，python的coroutines是基于`yield`操作进行的，由于进行`yield`时空出了cpu的使用，可以切换到其他的操作，借以实现并发操作。

> 协程: 协程，又称微线程，纤程，英文名Coroutine。协程的作用，是在执行函数A时，可以随时中断，去执行函数B，然后中断继续执行函数A（可以自由切换）。但这一过程并不是函数调用（没有调用语句），这一整个过程看似像多线程，然而协程只有一个线程执行.

python 3.4中内置了 asychio库，在python 3.5 中使用了`asych`, `await`关键字用以替代`@coroutines`,`yield/yield from`. python对于携程的支持，是基于一些特定规则的生成器。一个生成器的主要操作有以下三个：

* `close()`: 关闭一个生成器，并且raise GeneratorExit
* `send(arg)`:把一个参数送入生成器
* `next()`获取生成器的下一个元素

对于启动或者恢复一个生成器的执行，可以使用三方式：`next(), __next__(), g.send(None)`

### 5.2 `async`, `await`基于协程的异步调用

```python
async def func(param1, param2):
    do_stuff()
    await some_coroutine()
```

对于一个基本的异步函数，可以使用关键字`async`定义一个异步函数，直接调用一个异步函数，不会执行相应的结果而是返回一个`coroutine`对象。称之为`Navie coroutine`，除此之外，还可以使用装饰器`types.coroutine`,`asycnio.coroutine`获得一个navie coroutine。**注意对于`await`操作只可以等待一个naive coroutine**

对于一个异步函数的调用可以使用`send(None)`进行唤醒，因为其本质是一个generator：

```python
print(async_function().send(None))

StopIteration: 1
```

但是这样的直接调用会造成一个停止迭代的异常，可以定义一个如下的函数进行异常处理：（或者直接使用`asyncio.run()`）

```python
def run(coroutine):
    try:
        coroutine.send(None)
    except StopIteration as e:
        return e.value
```

在一个协程函数中，使用`await`关键字将自身挂起，并且等待其后的协程，等待其指向的协程结束后继续执行。`await`等待的协程对象必须是一个`Awaitable`的子类，只需要实现了`__await__`方法即可,主要有以下三种`Awaitable`, coroutine, Tasks, Futures。

```python
class Awaitable(metaclass=ABCMeta):
    __slots__ = ()

    @abstractmethod
    def __await__(self):
        yield

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Awaitable:
            return _check_methods(C, "__await__")
        return NotImplemented
```

而且可以看到，Coroutine类也继承了Awaitable，而且实现了send，throw和close方法。所以await一个调用异步函数返回的协程对象是合法的。

```python
class Coroutine(Awaitable):
    __slots__ = ()

    @abstractmethod
    def send(self, value):
        ...

    @abstractmethod
    def throw(self, typ, val=None, tb=None):
        ...

    def close(self):
        ...

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Coroutine:
            return _check_methods(C, '__await__', 'send', 'throw', 'close')
        return NotImplemented
```

使用`event_loop`执行coroutines

```python
loop = asyncio.get_event_loop()
res = loop.run_until_complete(asyncio.wait([buy_potatos(), buy_tomatos()]))
loop.close()
```

### 5.3 couroutine 高级API

**Aeaitable**：

* coroutine：可以指向两个相似的概念，协程函数（sync def）以及协程对象(由调用一个协程函数得到的对象)
* Tasks： 用以调控协程并发执行，可以使用一个`asycnio.create_task()`函数将一个协程包装为一个Task对象并且自动按顺序执行
* Futures： 低层API
* `asyncio.creat_task(coro)`:

  创建一个Task并且调度其执行
* `asyncio.sleep（ delay, result=None, *, loop=None）`:

  阻塞调用一定时间,如果给定了result，那么当该协程执行结束后将result返回给调用者。使用该操作可以挂起当前协程让其他协程获得cpu时间

**并发执行程序：**

* `asyncio.gather(*aws, loop=None, return_exceptions=None)`

  序列化执行`aws`中的Awaitable 对象，如果其中的任何一个为coroutine，那么自动转换为Task对象，如果所有的Awaitable对象都成功执行，那么返回一个于输入参数对应的返回值列表。如果gather被取消了，那么所有的提交的 Awaitable 都会被取消执行，但是其中的一个协程或者Future取消执行并不影响其他的 Awaitables。

### 5.4 异步生成器

看下AsyncGenerator的定义，它需要实现**aiter**和**anext**两个核心方法，以及asend，athrow，aclose方法。

```python
class AsyncGenerator(AsyncIterator):
    __slots__ = ()

    async def __anext__(self):
        ...

    @abstractmethod
    async def asend(self, value):
        ...

    @abstractmethod
    async def athrow(self, typ, val=None, tb=None):
        ...

    async def aclose(self):
        ...

    @classmethod
    def __subclasshook__(cls, C):
        if cls is AsyncGenerator:
            return _check_methods(C, '__aiter__', '__anext__',
                                  'asend', 'athrow', 'aclose')
        return NotImplemented
```

### 5.5 异步方法

对于类的普通方法或者是类方法都可以使用`async`关键字，将其定义为异步的。

```python
class ThreeTwoOne:
    @classmethod
    async def begin(cls):
        print(3)
        await asyncio.sleep(1)
        print(2)
        await asyncio.sleep(1)
        print(1)        
        await asyncio.sleep(1)
        return
```

完成异步的代码不一定要用async/await，使用了async/await的代码也不一定能做到异步，async/await是协程的语法糖，使协程之间的调用变得更加清晰，使用async修饰的函数调用时会返回一个协程对象，await只能放在async修饰的函数里面使用，await后面必须要跟着一个协程对象或Awaitable，await的目的是等待协程控制流的返回，而实现暂停并挂起函数的操作是yield。

## 6.装饰器

装饰器可以用于函数定义或者方法定义，也可以用于类的定义，可以使用多个装饰器对于一个方法或者类进行修饰，装饰器需要定义一个`wrapper()`函数,将一个需要被包含的函数传入装饰器，不需要传入`wrapper()`方法。对于装饰器需要的参数，在装饰器中传入。

```
@foo(arg)
@a
def fun(a1)
foo(arg)(a(fun(a1)))
```

```python
def use_logging(func):

    def wrapper():
        logging.warning(f"{func.__name__} is running")
        return func()
    return wrapper

@time_eval
@use_logging
def foo():
    print("i am foo")

foo()
```

常用的装饰器主要有：

* property: 将一个方法变为属性进行调用
* method\_name.setter: 将一个设置属性值的函数以属性的方式调用
* staticmethod： 静态方法，不需要 self， cls参数
* classmethod： 不需要self参数，需要cls参数

## 7. 类型标注

`typing`模块以暂定状态包含在标准库中。基本变量的类型注释可以使用`:`操作符，返回值`->`，与Rust相同。一些基本类型：

> from typing import List, Dict, Tuple, Squence

```python
from typing import List
Vector = List[float]

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

# typechecks; a list of floats qualifies as a Vector.
new_vector = scale(2.0, [1.0, -4.2, 5.4])
```

### 7.1 NewType

可以借助函数`typing.NewType`创建不同的新类型，静态类型检查器会将新类型视为原始类型的子类，对于帮助捕捉逻辑错误十分有用。

```python
def get_user_name(user_id: UserId) -> str:


UserId = NewType('UserId', int)

ProUserId = NewType('ProUserId', UserId)
# typechecks
user_a = get_user_name(UserId(42351))

# does not typecheck; an int is not a UserId
user_b = get_user_name(-1)
```

### 7.2 泛型

由于无法以通用方式静态推断有关保存在容器中的对象的类型信息，因此抽象基类已扩展为支持订阅以表示容器元素的预期类型。

```python
from typing import Mapping, Sequence

def notify_by_email(employees: Sequence[Employee],
                    overrides: Mapping[str, str]) -> None: ...
```

用户可以自定义泛型：[`Generic`](https://docs.python.org/zh-cn/3/library/typing.html#typing.Generic) 每个参数的类型变量必须是不同的

```python
from typing import TypeVar, Generic
from logging import Logger

T = TypeVar('T')

class LoggedVar(Generic[T]):
    def __init__(self, value: T, name: str, logger: Logger) -> None:
        self.name = name
        self.logger = logger
        self.value = value

    def set(self, new: T) -> None:
        self.log('Set ' + repr(self.value))
        self.value = new

    def get(self) -> T:
        self.log('Get ' + repr(self.value))
        return self.value

    def log(self, message: str) -> None:
        self.logger.info('%s: %s', self.name, message)
```


---

# 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://books.innohub.top/pyinfo/read_from_doc.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.
