# 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 as : # 代码块 ``` * ``:通常是一个上下文管理器对象,实现了`__enter__`和`__exit__`方法。 * ``:可选,用于存储由上下文管理器返回的对象。 `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 pyenv global 3.10 # 设置全局 pyenv global # 查看全局 pyenv shell # 设置当前shell pyenv local # 设置当前目录 生成 .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": { // 在这里添加环境变量(如果需要) } } ] } ```