794 lines
15 KiB
Markdown
794 lines
15 KiB
Markdown
# python高级
|
||
|
||
## 闭包
|
||
|
||
> 函数1嵌套函数2, 函数1返回函数2, 函数2使用了外部的变量
|
||
> 用处: 防止全局变量被外部修改
|
||
|
||
```python
|
||
def outer_function(x):
|
||
def inner_function(y):
|
||
return x + y
|
||
return inner_function
|
||
|
||
# 创建一个闭包
|
||
closure = outer_function(10)
|
||
|
||
# 闭包被使用
|
||
result = closure(5)
|
||
print(result) # 输出 15
|
||
```
|
||
|
||
* 如何要修改外部函数变量的值, 需要用nonlocal修饰
|
||
|
||
```python
|
||
def outer_function():
|
||
x = 10 # 外部函数中定义的变量
|
||
|
||
def inner_function():
|
||
nonlocal x # 使用 nonlocal 关键字声明 x 是外部函数中的变量
|
||
x += 5
|
||
print("内部函数中修改后的 x:", x)
|
||
|
||
inner_function()
|
||
print("外部函数中的 x:", x)
|
||
|
||
# 调用外部函数
|
||
outer_function()
|
||
`````
|
||
|
||
其他: global修饰
|
||
|
||
```python
|
||
global_variable = 10 # 全局变量
|
||
|
||
def modify_global_variable():
|
||
global global_variable # 使用 global 关键字声明在函数内部使用全局变量
|
||
global_variable += 5
|
||
|
||
modify_global_variable()
|
||
print(global_variable) # 输出 15,因为函数内部修改了全局变量的值
|
||
```
|
||
|
||
## 装饰器
|
||
|
||
> 在不破坏目标函数的功能前提下, 为目标函数添加功能
|
||
> 实际就是用闭包实现的
|
||
|
||
```python
|
||
def my_decorator(func):
|
||
def wrapper():
|
||
print("在调用被装饰函数之前的操作")
|
||
func() # 调用原始函数
|
||
print("在调用被装饰函数之后的操作")
|
||
|
||
return wrapper # 返回新的函数
|
||
|
||
@my_decorator # 使用装饰器语法
|
||
def say_hello():
|
||
print("Hello!")
|
||
|
||
# 调用被装饰后的函数
|
||
say_hello()
|
||
```
|
||
|
||
其实是语法糖
|
||
|
||
```python
|
||
def my_decorator(func):
|
||
def wrapper():
|
||
print("在调用被装饰函数之前的操作")
|
||
func() # 调用原始函数
|
||
print("在调用被装饰函数之后的操作")
|
||
|
||
return wrapper # 返回新的函数
|
||
|
||
|
||
def say_hello():
|
||
print("Hello!")
|
||
|
||
fn = my_decorator(say_hello)
|
||
fn()
|
||
```
|
||
|
||
* 把字典转化成对象
|
||
|
||
```python
|
||
from dataclasses import dataclass
|
||
|
||
@dataclass
|
||
class Torrent:
|
||
name: str
|
||
size: int
|
||
|
||
# 定义一个字典
|
||
torrent_dict = {
|
||
'name': 'example_torrent',
|
||
'size': 1024
|
||
}
|
||
|
||
# 使用字典初始化对象
|
||
torrent_obj = Torrent(**torrent_dict)
|
||
|
||
# 现在你可以访问对象的属性
|
||
print(torrent_obj.name)
|
||
print(torrent_obj.size)
|
||
```
|
||
|
||
## 设计模式
|
||
|
||
> 单例, 工厂, 建造者, 责任链, 状态, 备忘录, 解释器, 访问者, 观察者, 中介, 模板, 代理模式
|
||
|
||
### 单例模式
|
||
|
||
> 某些场景下,我们需要一个类无论获取多少次类对象,都仅仅提供一个具体的实例, 以节省创建类对象的开销和内存开销
|
||
> 定义: 保证一个类只有一个实例,并提供一个访问它的全局访问点
|
||
|
||
简单案例:
|
||
|
||
```python
|
||
class Tools:
|
||
pass
|
||
|
||
str_tools = Tools()
|
||
```
|
||
|
||
```python
|
||
from test import str_tools
|
||
s1 = str_tools()
|
||
s2 = str_tools()
|
||
print(s1)
|
||
print(s2)
|
||
# s1 和 s2 是同一个对象
|
||
```
|
||
|
||
案例2:
|
||
|
||
```python
|
||
def singleton(cls):
|
||
instances = {} # 存储实例的字典
|
||
|
||
def get_instance(*args, **kwargs):
|
||
if cls not in instances:
|
||
# 如果实例不存在,创建一个新的实例
|
||
instances[cls] = cls(*args, **kwargs)
|
||
return instances[cls]
|
||
|
||
return get_instance
|
||
|
||
@singleton
|
||
class MyClass:
|
||
def __init__(self, name):
|
||
self.name = name
|
||
|
||
# 创建两个实例,实际上是同一个实例
|
||
obj1 = MyClass("Instance 1")
|
||
obj2 = MyClass("Instance 2")
|
||
|
||
# 输出实例的名称
|
||
print(obj1.name) # 输出 "Instance 1"
|
||
print(obj2.name) # 输出 "Instance 1",因为实际上是同一个实例
|
||
|
||
```
|
||
|
||
### 工厂模式
|
||
|
||
> 大量创建对象时, 提供一个统一的入口
|
||
> 当发生变化的时候, 仅修改工厂类的创建方法
|
||
|
||
```python
|
||
class Dog:
|
||
def speak(self):
|
||
return "汪汪汪!"
|
||
|
||
class Cat:
|
||
def speak(self):
|
||
return "喵喵喵!"
|
||
|
||
class AnimalFactory:
|
||
def create_animal(self, animal_type):
|
||
if animal_type == "Dog":
|
||
return Dog()
|
||
elif animal_type == "Cat":
|
||
return Cat()
|
||
else:
|
||
raise ValueError("不支持的动物类型")
|
||
|
||
# 使用工厂创建对象
|
||
factory = AnimalFactory()
|
||
|
||
dog = factory.create_animal("Dog")
|
||
cat = factory.create_animal("Cat")
|
||
|
||
# 调用对象的方法
|
||
print(dog.speak()) # 输出 "汪汪汪!"
|
||
print(cat.speak()) # 输出 "喵喵喵!"
|
||
|
||
```
|
||
|
||
## 多线程
|
||
|
||
```python
|
||
import threading
|
||
|
||
thread_obj = threading.Thread([group [, target [, name [, args [, kwargs]]]]])
|
||
# group 暂时无用, 未来功能的预留参数
|
||
# target 执行的任务目标名
|
||
# args 以元组方式传参
|
||
# kwargs 以字典方式传参
|
||
# name 线程名, 一般不用设置
|
||
|
||
thread_obj.start()
|
||
```
|
||
|
||
## socket
|
||
|
||
需要服务端+客户端
|
||
|
||
### 服务端
|
||
|
||
```python
|
||
# 服务端
|
||
# 1. 创建socket对象
|
||
import socket
|
||
|
||
socket_server = socket.socket()
|
||
|
||
# 2. 服务端绑定ip和端口
|
||
socket_server.bind(('0.0.0.0', 8888))
|
||
|
||
# 3. 监听端口, 括号内为连接数量, 不填会自动设置一个合理的值
|
||
socket_server.listen(5)
|
||
|
||
# 4. 接受客户端连接
|
||
client, addr = socket_server.accept()
|
||
print('连接地址:', addr)
|
||
# accept()方法会阻塞, 直到有客户端连接才会往下执行Í
|
||
# accept()返回二元元组, 可以用两个变量接收
|
||
|
||
# 5. recv接收
|
||
while True:
|
||
data = client.recv(1024).decode("UTF-8")
|
||
# recv()参数为缓存区大小, 设置为1024, 代表缓存区为1024字节
|
||
# recv()返回值是字节数组(bytes对象), 需要decode()方法将字节数组转为字符串
|
||
print(f"接收的数据: {data}")
|
||
if data == "exit":
|
||
break
|
||
# 6. send发送数据
|
||
client.send(f"已接收数据: {data}".encode("UTF-8"))
|
||
|
||
# 7. 关闭连接
|
||
client.close()
|
||
socket_server.close()
|
||
|
||
# 客户端可以先通过 https://github.com/nicedayzhu/netAssist/releases 测试
|
||
```
|
||
|
||
### 客户端
|
||
|
||
```python
|
||
# 1. 创建服务
|
||
import socket
|
||
socket_client = socket.socket()
|
||
|
||
# 2.建立连接
|
||
socket_client.connect(('127.0.0.1', 8888))
|
||
|
||
while True:
|
||
msg = input("请输入要发送的数据: ")
|
||
# 3. 发消息
|
||
socket_client.send(msg.encode("UTF-8"))
|
||
if msg == "exit":
|
||
break
|
||
# 4. 接收消息
|
||
# recv 是阻塞式的
|
||
data = socket_client.recv(1024).decode("UTF-8")
|
||
print(f"接收的数据: {data}")
|
||
|
||
# 5. 关闭
|
||
socket_client.close()
|
||
```
|
||
|
||
===服务端先接收再发送, 服务端先发送再接收===
|
||
|
||
## 异步
|
||
|
||
### httpx
|
||
|
||
### asyncio
|
||
|
||
## with as
|
||
|
||
```python
|
||
with <expression> as <variable>:
|
||
# 代码块
|
||
```
|
||
|
||
* `<expression>`:通常是一个上下文管理器对象,实现了`__enter__`和`__exit__`方法。
|
||
* `<variable>`:可选,用于存储由上下文管理器返回的对象。
|
||
|
||
`with`语句在进入和退出代码块时分别调用对象的`__enter__`和`__exit__`方法,这样可以确保资源在合适的时候被正确获取和释放。例如,在处理文件时:
|
||
|
||
```python
|
||
with open('file.txt', 'r') as file:
|
||
content = file.read()
|
||
# 在这里使用文件内容进行操作
|
||
# 在此处退出上下文,文件会被正确关闭
|
||
|
||
```
|
||
|
||
## 列表推导式
|
||
|
||
```python
|
||
[expression for item in iterable if condition]
|
||
```
|
||
|
||
* expression:对可迭代对象中每个元素进行操作或处理的表达式。
|
||
* item:可迭代对象中的每个元素。
|
||
* iterable:用于迭代的对象,如列表、元组、集合等。
|
||
* condition:可选的条件,用于过滤元素。如果条件为真,元素将包含在最终的列表中
|
||
|
||
## 设置pip 镜像
|
||
|
||
```sh
|
||
mkdir ~/.pip
|
||
vim ~/.pip/pip.conf
|
||
|
||
===
|
||
[global]
|
||
trusted-host = mirrors.aliyun.com
|
||
index-url = http://mirrors.aliyun.com/pypi/simple
|
||
===
|
||
```
|
||
|
||
### 生成`requirements.txt`
|
||
|
||
```python
|
||
pip install pipreqs
|
||
pipreqs .
|
||
```
|
||
|
||
### 引用
|
||
|
||
```txt
|
||
-r demo/requirements.txt
|
||
```
|
||
|
||
## 版本管理和虚拟环境
|
||
|
||
### pyenv 版本管理
|
||
|
||
```sh
|
||
brew install pyenv
|
||
|
||
# homebrew安装的python环境变量可以屏蔽了
|
||
# 比教程多了 $PYENV_ROOT/shims
|
||
|
||
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
|
||
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:$PATH"' >> ~/.zshrc
|
||
echo 'eval "$(pyenv init -)"' >> ~/.zshrc
|
||
|
||
pyenv install -l
|
||
pyenv install 3.10.4
|
||
|
||
pyenv uninstall <versions>
|
||
|
||
pyenv global 3.10 # 设置全局
|
||
pyenv global # 查看全局
|
||
|
||
pyenv shell <version> # 设置当前shell
|
||
|
||
pyenv local <version> # 设置当前目录 生成 .python-version 文件
|
||
pyenv local # 查看当前目录, 读取 .python-version 文件
|
||
|
||
# 查看已安装的版本
|
||
pyenv versions
|
||
# 查看当前目录的版本
|
||
pyenv version
|
||
```
|
||
|
||
### venv 自带虚拟环境
|
||
|
||
venv 是python 3.3后官方自带的, 在这之前就有第三方virtualenv
|
||
|
||
缺点: 只能使用当前系统已经安装好的python,无法使用其他版本的python环境
|
||
|
||
```sh
|
||
# 添加虚拟环境
|
||
python3 -m venv .venv
|
||
# 激活
|
||
source .venv/bin/activate
|
||
# 正常使用pip
|
||
pip install -r requirements.txt
|
||
# 取消激活
|
||
deactivate
|
||
```
|
||
|
||
* vscode 自动激活
|
||
|
||
`"python.terminal.activateEnvInCurrentTerminal": true`
|
||
|
||
cmd+shift+p : python create env
|
||
|
||
看vscode下方状态栏是否显示env和python版本
|
||
|
||
* zsh 钩子自动激活
|
||
|
||
```sh
|
||
vim ~/.zshrc
|
||
|
||
# 定义 chpwd 钩子
|
||
chpwd_hook() {
|
||
# 检查当前目录中是否存在 venv 或 .venv 目录
|
||
if [ -d "venv" ]; then
|
||
# 如果存在 venv 目录,则激活虚拟环境
|
||
source venv/bin/activate
|
||
echo '激活python环境'
|
||
elif [ -d ".venv" ]; then
|
||
# 如果存在 .venv 目录,则激活虚拟环境
|
||
source .venv/bin/activate
|
||
echo '激活python环境'
|
||
fi
|
||
}
|
||
|
||
# 将 chpwd_hook 设置为 chpwd 钩子
|
||
chpwd_functions+=(chpwd_hook)
|
||
```
|
||
|
||
* direnv
|
||
|
||
https://direnv.net/docs/hook.html
|
||
|
||
```sh
|
||
brew install direnv
|
||
|
||
# 添加到~/.zshrc
|
||
eval "$(direnv hook zsh)"
|
||
# 关闭提示
|
||
export DIRENV_LOG_FORMAT=
|
||
|
||
# 在项目下新建.envrc
|
||
echo 'source $PWD/.venv/bin/activate' > .envrc
|
||
|
||
# 允许当项目下加载配置
|
||
direnv allow
|
||
```
|
||
|
||
常见函数:
|
||
|
||
https://direnv.net/man/direnv-stdlib.1.html
|
||
|
||
`PATH_add bin`
|
||
|
||
### uv 包管理器
|
||
|
||
> 用来代替 pip pip-tools virtualenv 命令
|
||
> 有全局缓存
|
||
|
||
```sh
|
||
brew install uv
|
||
|
||
# 创建虚拟环境
|
||
uv venv
|
||
source .venv/bin/activate
|
||
|
||
# 或者不激活直接 uv run xx.py
|
||
|
||
uv pip install xxx
|
||
uv pip install -r requirements.txt
|
||
uv pip sync requirements.txt
|
||
|
||
# 生成依赖列表
|
||
uv pip freeze > requirements.txt
|
||
|
||
# 指定系统中某个版本的python
|
||
PYENV_VERSION=3.12.2 uv venv
|
||
--python 参数可以指定pyton路径
|
||
```
|
||
|
||
* 设置镜像
|
||
|
||
```sh
|
||
export UV_DEFAULT_INDEX="http://mirrors.aliyun.com/pypi/simple"
|
||
```
|
||
|
||
* macos 使用硬链接
|
||
|
||
```sh
|
||
export UV_LINK_MODE="hardlink"
|
||
```
|
||
|
||
### rye 虚拟环境和包管理
|
||
|
||
包管理可以使用 `uv`
|
||
|
||
### miniconda/miniforge
|
||
|
||
适合公司使用, vscode不会自动切换环境
|
||
|
||
miniforge是社区免费版, miniconda是公司产品
|
||
|
||
```sh
|
||
brew install miniforge
|
||
|
||
# 设置镜像源
|
||
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
|
||
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
|
||
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
|
||
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
|
||
conda config --set show_channel_urls yes
|
||
conda config --show channels
|
||
conda update --all
|
||
|
||
# 默认创建了一个base的环境
|
||
|
||
# 用Python 3.9创建一个名为 f0x 的虚拟环境
|
||
conda create -n f0x python=3.9
|
||
|
||
# 查看所有环境
|
||
conda info --envs
|
||
conda env list
|
||
|
||
# 激活虚拟环境
|
||
conda activate f0x
|
||
|
||
# 安装库
|
||
conda install jupyter
|
||
```
|
||
|
||
### poetry 虚拟环境和包管理
|
||
|
||
https://blog.kyomind.tw/python-poetry/
|
||
|
||
https://python-poetry.org/docs/main/configuration/
|
||
|
||
```sh
|
||
curl -sSL https://install.python-poetry.org | python3 -
|
||
# 安装路径 $HOME/.local/bin
|
||
# poetry 要安装在单独的虚拟环境中, 因为它自己就带一堆包, 并且会依赖特定版本的python
|
||
# 卸载
|
||
curl -sSL https://install.python-poetry.org | POETRY_UNINSTALL=1 python3 -
|
||
|
||
vim ~/.zshrc
|
||
===
|
||
alias p="poetry"
|
||
===
|
||
source ~/.zshrc
|
||
|
||
# 初始化
|
||
poetry init
|
||
|
||
# 安装环境当前工作目录下
|
||
poetry config virtualenvs.in-project true
|
||
# 查看配置
|
||
poetry config --list
|
||
|
||
poetry env use python
|
||
# poetry env use python3
|
||
# poetry env use python3.8
|
||
# poetry env use 3.8
|
||
poetry env remove python
|
||
|
||
# 进入虚拟环境
|
||
poetry shell
|
||
# 退出
|
||
exit
|
||
|
||
# 相当于pip list
|
||
poetry show
|
||
poetry show --tree
|
||
poetry show 模块名 --tree
|
||
|
||
# 不进入shell也可以安装
|
||
poetry add 模块名
|
||
poetry remove 模块名
|
||
# 安装到开发环境
|
||
poetry add 模块名 --group dev
|
||
|
||
# 更新所有模块
|
||
poetry update
|
||
# 更新指定模块
|
||
poetry update 模块名
|
||
|
||
# 手动修改pyproject.toml后不会自动更新lock, 需要执行
|
||
poetry lock
|
||
poetry install --sync
|
||
|
||
# 导入requirements.txt
|
||
cat requirements.txt | xargs poetry add
|
||
|
||
# 导出requirements
|
||
poetry export -f requirements.txt -o requirements.txt --without-hashes
|
||
|
||
# 按照 pyproject.toml 安装
|
||
poetry install
|
||
|
||
# 不进入环境执行运行
|
||
poetry run python3 main.py
|
||
|
||
# 更多指令
|
||
poetry list
|
||
|
||
# vscode 使用
|
||
poetry env info
|
||
poetry env info --path
|
||
# 手动指定python路径
|
||
```
|
||
|
||
## json
|
||
|
||
```python
|
||
# 内置模块 json
|
||
import json
|
||
|
||
data = [{"name":"张三", "age":18, "gender":"男"}]
|
||
|
||
# py数据转json
|
||
data = json.dumps(data)
|
||
print(data)
|
||
print(type(data))
|
||
|
||
# 中文变乱码
|
||
data = json.dumps(data, ensure_ascii=False)
|
||
print(data)
|
||
print(type(data))
|
||
|
||
# json转py数据
|
||
data = json.loads(data)
|
||
print(data)
|
||
print(type(data))
|
||
```
|
||
|
||
## 三目运算符
|
||
|
||
```python
|
||
x if condition else y
|
||
|
||
# 示例
|
||
|
||
x = 5
|
||
result = "Even" if x % 2 == 0 else "Odd"
|
||
print(result)
|
||
```
|
||
|
||
## 逻辑短路
|
||
|
||
```python
|
||
# and 运算符的逻辑短路
|
||
result_and = False and some_function() # some_function() 不会被调用
|
||
|
||
# or 运算符的逻辑短路
|
||
result_or = True or some_function() # some_function() 不会被调用
|
||
|
||
```
|
||
|
||
## **pycache**
|
||
|
||
python编译成字节码的产物, 直接vscode和gitignore忽略掉
|
||
|
||
## 锁
|
||
|
||
```python
|
||
import threading
|
||
|
||
# 创建一个锁
|
||
lock = threading.Lock()
|
||
|
||
# 使用锁来保护共享资源
|
||
shared_resource = 0
|
||
|
||
def update_shared_resource():
|
||
global shared_resource
|
||
with lock:
|
||
shared_resource += 1
|
||
|
||
# 创建多个线程来同时更新共享资源
|
||
threads = []
|
||
for _ in range(5):
|
||
t = threading.Thread(target=update_shared_resource)
|
||
threads.append(t)
|
||
t.start()
|
||
|
||
# 等待所有线程执行完毕
|
||
for t in threads:
|
||
t.join()
|
||
|
||
print(shared_resource) # 应该输出 5
|
||
```
|
||
|
||
* 装饰器写法
|
||
|
||
```python
|
||
def locked(func):
|
||
def wrapper(*args, **kwargs):
|
||
with lock:
|
||
return func(*args, **kwargs)
|
||
|
||
return wrapper
|
||
```
|
||
|
||
## 代码检查工具
|
||
|
||
* `ruff`
|
||
|
||
```sh
|
||
ruff src
|
||
# 递归检查
|
||
ruff src -- --recursive
|
||
# 修复
|
||
ruff src -- --recursive --fix
|
||
```
|
||
|
||
vscode 有 ruff 插件
|
||
|
||
* blank
|
||
|
||
```sh
|
||
black --check src
|
||
black src
|
||
```
|
||
|
||
## 测试
|
||
|
||
* pytest
|
||
|
||
```python
|
||
# 在src目录下创建test目录
|
||
# 在test目录下创建 __init__.py
|
||
# 测试脚本文件名 test_xxx.py, test_开头
|
||
from module.xxxx import xxxxx
|
||
|
||
def test_xxx():
|
||
# 使用assert断言
|
||
assert A == B
|
||
```
|
||
|
||
测试
|
||
|
||
```sh
|
||
cd src
|
||
pytest test
|
||
|
||
# 需要显示print
|
||
pytest -s test
|
||
```
|
||
|
||
### 进度条
|
||
|
||
```python
|
||
x = 10000
|
||
for i in range(x):
|
||
for j in range(x):
|
||
k = j * i
|
||
print(f"{i + 1} / {x}", end="\r")
|
||
print("\n完成!")
|
||
```
|
||
|
||
`end="\r"`表示每次输出后都不换行
|
||
|
||
第三方库:
|
||
|
||
* tqdm
|
||
* progress
|
||
|
||
## python vscode调试
|
||
|
||
```json
|
||
{
|
||
"version": "0.2.0",
|
||
"configurations": [
|
||
{
|
||
"name": "Python: Main",
|
||
"type": "python",
|
||
"request": "launch",
|
||
"program": "${workspaceFolder}/main.py", // main.py 的路径
|
||
"console": "integratedTerminal",
|
||
"justMyCode": false, // 设置为 false 以允许在子模块中设置断点
|
||
"env": {
|
||
// 在这里添加环境变量(如果需要)
|
||
}
|
||
}
|
||
]
|
||
}
|
||
|
||
``` |