2025-03-29 14:35:49 +08:00

794 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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": {
// 在这里添加环境变量(如果需要)
}
}
]
}
```