Name | cognihub-pyeffectref JSON |
Version |
0.2.0
JSON |
| download |
home_page | None |
Summary | A reactive programming library inspired by Vue 3's Composition API, featuring low-level (Ref/effect) and high-level (ReactiveDict/ReadOnlyView) interfaces |
upload_time | 2025-07-23 07:52:00 |
maintainer | None |
docs_url | None |
author | None |
requires_python | >=3.10 |
license | MIT License
Copyright (c) 2025 HUANG SIZHE
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
|
keywords |
reactive
vue
effect
ref
cognihub
reactivedict
readonly
async
threading
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
|
# CogniHub PyEffectRef
CogniHub 项目的响应式编程库,提供类似 Vue 3 Composition API 的响应式系统.本库分为**底层接口**和**高级接口**两个层次,满足不同复杂度的使用需求.
## 特性
- 🔄 **响应式编程**: 类似 Vue 3 Composition API 的响应式系统
- 🔒 **线程安全**: 支持多线程环境下的安全操作
- ⚡ **异步支持**: 完整支持 asyncio 协程环境
- 🎯 **类型提示**: 完整的 TypeScript 风格类型提示支持
- 🏗️ **分层设计**: 底层接口(Ref/effect) + 高级接口(ReactiveDict/ReadOnlyView)
- 🎛️ **执行控制**: 支持同步、异步、顺序执行等多种模式
## 安装
```bash
pip install cognihub-pyeffectref
```
或从源码安装:
```bash
git clone https://github.com/hsz1273327/cognihub_pyeffectref.git
cd cognihub_pyeffectref
pip install -e .
```
## 📚 架构概览
本库采用分层设计,提供两个层次的接口:
### 🔧 底层接口 (Low-level APIs)
- **`Ref[T]`**: 响应式数据容器,支持泛型类型指定
- **`effect`**: 副作用装饰器,自动追踪依赖关系
- **`ReadOnlyRef[T]`**: 只读响应式引用
**特点**: 直接使用泛型指定类型,适合简单数据结构和性能敏感场景
### 🏗️ 高级接口 (High-level APIs)
- **`ReactiveDict`**: 响应式字典,支持嵌套结构,配合 TypedDict 和 cast 使用
- **`ReadOnlyView`**: 只读视图,配合 Protocol 和 cast 使用
**特点**: 需要结合 `TypedDict` 和 `Protocol` 指定复杂类型结构,适合复杂应用场景
## 🚀 快速开始
### 1️⃣ 底层接口示例
#### 基本用法 - 泛型类型指定
```python
from cognihub_pyeffectref import Ref, effect
from typing import List, Dict
# 使用泛型指定类型
count: Ref[int] = Ref(0)
name: Ref[str] = Ref("Alice")
items: Ref[List[str]] = Ref(["apple", "banana"])
# 创建副作用函数
@effect
def log_count() -> None:
print(f"Count is: {count.value}")
@effect
def log_greeting() -> None:
print(f"Hello, {name.value}!")
# 初始执行
log_count() # 输出: Count is: 0
log_greeting() # 输出: Hello, Alice!
# 修改数据,自动触发副作用
count.value = 5 # 输出: Count is: 5
name.value = "Bob" # 输出: Hello, Bob!
```
#### 同步/异步/多线程支持
默认单线程同步执行,通过`Ref.configure_sync_task_executor`可配置为异步或多线程执行.
**注意:**
1. 请仅在需要时使用异步或多线程,因为这会增加复杂性和调试难度.
2. 仅在应用入口处设置一次执行器配置即可,不需要在每个模块中重复设置.设置接口是一次性的,一旦设置过了就无法改变
```python
import asyncio
import threading
import time
from cognihub_pyeffectref import Ref, effect
data: Ref[str] = Ref("initial")
# 1. 同步使用
@effect
def sync_effect() -> None:
print(f"Sync effect: {data.value}")
# 2. 异步使用
@effect
async def async_effect() -> None:
print(f"Async effect: {data.value}")
await asyncio.sleep(0.1) # 异步操作
# 3. 多线程使用
def thread_worker(thread_id: int) -> None:
@effect
def thread_effect() -> None:
print(f"Thread {thread_id} effect: {data.value}")
thread_effect() # 建立依赖
time.sleep(0.1)
async def main():
# 同步执行
sync_effect()
# 异步执行
await async_effect()
# 多线程执行
threads = []
for i in range(3):
thread = threading.Thread(target=thread_worker, args=(i,))
thread.start()
threads.append(thread)
# 触发所有副作用
data.value = "updated"
await asyncio.sleep(0.2) # 等待异步和线程完成
for thread in threads:
thread.join()
# asyncio.run(main())
```
#### 执行器配置 - 控制回调执行方式
> 默认的回调执行方式:
1. 在未设置执行器时
1. 同步回调在当前线程同步执行.
2. 异步回调在 asyncio 事件循环中异步执行.
2. 在设置了执行器的情况下
1. 同步回调会在执行器中多线程后台执行.
2. 异步回调会在 asyncio 事件循环中异步执行.
> 实例的精细化回调执行控制
实例可以在初始化时额外指定其用`subscribe`注册的同步回调函数的执行方式.
1. `subscribe_immediate=True`: 强制在当前线程同步执行回调,忽略全局执行器配置.
2. `subscribe_sequential=True`: 保证同步回调函数按注册顺序在后台执行,适用于需要顺序执行的场景.
```python
import concurrent.futures
from cognihub_pyeffectref import Ref
# 配置全局执行器 - 使用 asyncio 线程池
Ref.configure_sync_task_executor('asyncio')
# 或使用自定义线程池
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
Ref.configure_sync_task_executor(executor)
# 强制立即同步执行 (忽略执行器配置)
immediate_ref = Ref(0, subscribe_immediate=True)
# 保证顺序执行 (在执行器中按顺序执行)
sequential_ref = Ref(0, subscribe_sequential=True)
```
### 2️⃣ 高级接口示例
#### ReactiveDict - 结合 TypedDict 指定结构
```python
from cognihub_pyeffectref import ReactiveDict, effect
from typing import TypedDict, cast
# 1. 定义数据结构
class UserData(TypedDict):
name: str
email: str
age: int
is_active: bool
# 2. 创建响应式字典
user_dict = ReactiveDict({
'name': 'Alice',
'email': 'alice@example.com',
'age': 25,
'is_active': True
})
# 3. 类型转换以获得类型提示
user: UserData = cast(UserData, user_dict)
# 4. 使用时享受完整类型提示
@effect
def watch_user() -> None:
print(f"User: {user['name']} ({user['age']})")
print(f"Email: {user['email']}")
print(f"Active: {user['is_active']}")
watch_user()
# 5. 修改数据
user['name'] = 'Bob' # 自动触发副作用
user['age'] = 26 # 自动触发副作用
```
#### ReadOnlyView - 结合 Protocol 创建只读视图
```python
from cognihub_pyeffectref import ReactiveDict, ReadOnlyView, ReadOnlyRef, effect
from typing import Protocol, cast, Any
# 1. 定义 Protocol 描述只读视图结构
class UserViewProtocol(Protocol):
name: ReadOnlyRef[str]
email: ReadOnlyRef[str]
age: ReadOnlyRef[int]
is_active: ReadOnlyRef[bool]
def __call__(self) -> dict[str, Any]: ...
# 2. 创建原始数据
user_data = ReactiveDict({
'name': 'Alice',
'email': 'alice@example.com',
'age': 25,
'is_active': True
})
# 3. 创建只读视图
user_view = cast(UserViewProtocol, ReadOnlyView(user_data))
# 4. 只读访问 - 享受完整类型提示和防护
@effect
def watch_user_view() -> None:
print(f"Name: {user_view.name.value}")
print(f"Email: {user_view.email.value}")
print(f"Age: {user_view.age.value}")
watch_user_view()
# 5. 无法修改 (编译时和运行时都会报错)
# user_view.name.value = "Bob" # AttributeError: ReadOnlyRef has no setter
# user_view['name'] = "Bob" # TypeError: ReadOnlyView is not subscriptable
# 6. 只能通过原始数据修改
user_data.name = "Bob" # 自动触发只读视图的副作用
```
#### 复杂嵌套数据结构
```python
from cognihub_pyeffectref import ReactiveDict, ReadOnlyView, ReadOnlyRef, effect
from typing import Protocol, cast, Any
# 1. 定义嵌套的 Protocol 结构
class DatabaseConfig(Protocol):
host: ReadOnlyRef[str]
port: ReadOnlyRef[int]
name: ReadOnlyRef[str]
class ApiConfig(Protocol):
base_url: ReadOnlyRef[str]
timeout: ReadOnlyRef[int]
retry_count: ReadOnlyRef[int]
class AppConfig(Protocol):
database: DatabaseConfig
api: ApiConfig
debug_mode: ReadOnlyRef[bool]
def __call__(self) -> dict[str, Any]: ...
# 2. 创建嵌套数据
config_data = ReactiveDict({
'database': {
'host': 'localhost',
'port': 5432,
'name': 'myapp'
},
'api': {
'base_url': 'https://api.example.com',
'timeout': 30,
'retry_count': 3
},
'debug_mode': False
})
# 3. 创建类型化的只读视图
config_view = cast(AppConfig, ReadOnlyView(config_data))
# 4. 访问嵌套数据 - 完整类型提示
@effect
def watch_config() -> None:
db_host = config_view.database.host.value
api_url = config_view.api.base_url.value
debug = config_view.debug_mode.value
print(f"Database: {db_host}")
print(f"API: {api_url}")
print(f"Debug: {debug}")
watch_config()
# 5. 修改原始数据触发变更
config_data.database.host = 'production-db'
config_data.debug_mode = True
```
## 📖 API 参考
### 🔧 底层接口 (Low-level APIs)
#### Ref[T]
响应式数据容器类,支持泛型类型指定.
**构造函数**
- `Ref(initial_value: T, subscribe_immediate: bool = False, subscribe_sequential: bool = False)`
- `initial_value`: 初始值
- `subscribe_immediate`: 是否强制在当前线程同步执行回调 (忽略全局执行器配置)
- `subscribe_sequential`: 是否保证回调按注册顺序执行
**属性**
- `value: T`: 获取或设置引用的值
**方法**
- `subscribe(callback: Callable[[T, T], None])`: 订阅值变化
- `unsubscribe(callback: Callable[[T, T], None])`: 取消订阅
- `configure_sync_task_executor(executor)`: 配置全局同步任务执行器
**类方法**
- `Ref.configure_sync_task_executor('asyncio' | ThreadPoolExecutor)`: 配置全局执行器
#### ReadOnlyRef[T]
只读响应式引用,从 Ref 创建.
**构造函数**
- `ReadOnlyRef(ref: Ref[T])`: 从现有 Ref 创建只读引用
**属性**
- `value: T`: 只读访问引用的值 (无 setter)
**方法**
- `subscribe(callback: Callable[[T, T], None])`: 订阅值变化
- `unsubscribe(callback: Callable[[T, T], None])`: 取消订阅
#### effect
副作用装饰器,自动追踪 Ref 依赖关系.
```python
@effect
def my_effect() -> None:
# 访问 Ref.value 会自动建立依赖关系
pass
# 或手动调用
effect_wrapper = effect(my_function)
effect_wrapper()
```
#### EffectWrapper
effect 装饰器返回的包装器类.
**方法**
- `stop()`: 停止副作用,使其不再响应数据变化
### 🏗️ 高级接口 (High-level APIs)
#### ReactiveDict
响应式字典,支持嵌套结构和动态键.
**构造函数**
- `ReactiveDict(initial_data: dict = None)`: 创建响应式字典
**方法**
- `to_dict() -> dict`: 转换为普通字典
- `keys()`, `values()`, `items()`: 字典接口方法
- `get(key, default=None)`: 获取值
- `pop(key, default=None)`: 删除并返回值
- `clear()`: 清空字典
- `update(other)`: 更新字典
**特性**
- 支持嵌套结构自动转换为 ReactiveDict
- 动态属性访问: `obj.key` 等价于 `obj['key']`
- 与 TypedDict 结合使用获得类型提示
#### ReadOnlyView
只读视图,从 ReactiveDict 创建结构化的只读访问.
**构造函数**
- `ReadOnlyView(reactive_dict: ReactiveDict)`: 创建只读视图
**特性**
- 递归将所有值转换为 ReadOnlyRef
- 支持嵌套结构的只读访问
- 与 Protocol 结合获得完整类型提示
- 调用 `view()` 返回当前状态的字典快照
**使用模式**
```python
# 1. 定义结构 Protocol
class MyDataProtocol(Protocol):
field: ReadOnlyRef[str]
def __call__(self) -> dict[str, Any]: ...
# 2. 创建并转换
data = ReactiveDict({'field': 'value'})
view = cast(MyDataProtocol, ReadOnlyView(data))
# 3. 类型安全访问
print(view.field.value) # 完整类型提示
snapshot = view() # 获取当前状态快照
```
## ⚡ 执行模式详解
### 同步执行 (默认)
```python
from cognihub_pyeffectref import Ref, effect
# 默认模式:回调在当前线程中执行
data = Ref("initial")
@effect
def sync_effect() -> None:
print(f"Current value: {data.value}")
sync_effect() # 立即执行
data.value = "changed" # 在当前线程中触发回调
```
### 异步执行 (asyncio 环境)
```python
import asyncio
from cognihub_pyeffectref import Ref, effect
# 配置使用 asyncio 线程池
Ref.configure_sync_task_executor('asyncio')
data = Ref("initial")
@effect
async def async_effect() -> None:
print(f"Async value: {data.value}")
await asyncio.sleep(0.1)
async def main():
await async_effect() # 建立依赖
data.value = "changed" # 回调将在 asyncio.to_thread 中执行
await asyncio.sleep(0.2) # 等待异步回调完成
asyncio.run(main())
```
### 多线程执行
```python
import threading
import concurrent.futures
from cognihub_pyeffectref import Ref, effect
# 配置自定义线程池
executor = concurrent.futures.ThreadPoolExecutor(max_workers=4)
Ref.configure_sync_task_executor(executor)
data = Ref(0)
@effect
def thread_effect() -> None:
thread_id = threading.get_ident()
value = data.value
print(f"Thread {thread_id}: {value}")
# 在多个线程中建立依赖
threads = []
for i in range(3):
thread = threading.Thread(target=thread_effect)
thread.start()
threads.append(thread)
# 触发回调 - 将在线程池中执行
data.value = 42
for thread in threads:
thread.join()
executor.shutdown(wait=True)
```
### 执行控制选项
```python
from cognihub_pyeffectref import Ref
# 1. 强制立即同步执行 (忽略全局执行器)
immediate_ref = Ref(0, subscribe_immediate=True)
# 2. 保证顺序执行 (在执行器中按注册顺序执行)
sequential_ref = Ref(0, subscribe_sequential=True)
# 3. 组合使用 (immediate 优先级更高)
combined_ref = Ref(0, subscribe_immediate=True, subscribe_sequential=True)
```
## 🔒 线程安全特性
本库在设计时充分考虑了线程安全:
### 内部锁机制
- `Ref` 使用内部锁保护订阅者集合的并发修改
- 支持在多线程环境中安全地读写响应式数据
- `ReactiveDict` 的嵌套操作也是线程安全的
### 上下文隔离
- 异步环境使用 `contextvars` 隔离 effect 上下文
- 多线程环境使用 `threading.local` 进行线程隔离
- 确保副作用函数在正确的上下文中执行
### 执行器配置
- 全局执行器配置支持 `'asyncio'` 和自定义 `ThreadPoolExecutor`
- `subscribe_immediate=True` 可强制在当前线程同步执行,提供确定性行为
- `subscribe_sequential=True` 保证回调按注册顺序执行,避免竞态条件
## 🎯 使用场景推荐
### 何时使用底层接口 (Ref/effect)
✅ **适合场景**:
- 简单的响应式状态管理
- 性能敏感的应用
- 需要精确控制执行时机
- 与现有代码集成
```python
# 适合:简单计数器
counter: Ref[int] = Ref(0)
user_name: Ref[str] = Ref("Anonymous")
```
### 何时使用高级接口 (ReactiveDict/ReadOnlyView)
✅ **适合场景**:
- 复杂的嵌套数据结构
- 需要结构化的数据访问
- 配置管理系统
- 大型应用的状态管理
```python
# 适合:复杂配置对象
app_config = ReactiveDict({
'database': {'host': 'localhost', 'port': 5432},
'cache': {'ttl': 3600, 'max_size': 1000},
'features': {'debug': False, 'analytics': True}
})
```
### 混合使用模式
```python
# 组合使用:底层接口处理核心状态,高级接口管理配置
from cognihub_pyeffectref import Ref, ReactiveDict, ReadOnlyView, effect
from typing import Protocol, cast
# 底层:核心应用状态
app_state: Ref[str] = Ref("initializing")
user_count: Ref[int] = Ref(0)
# 高级:复杂配置管理
config_data = ReactiveDict({
'ui': {'theme': 'dark', 'language': 'en'},
'api': {'timeout': 30, 'retries': 3}
})
class ConfigProtocol(Protocol):
ui: dict[str, str]
api: dict[str, int]
config = cast(ConfigProtocol, config_data)
@effect
def sync_state() -> None:
state = app_state.value
theme = config['ui']['theme']
print(f"App {state} with {theme} theme")
sync_state()
```
## 🛠️ 开发指南
### 环境设置
```bash
# 克隆仓库
git clone https://github.com/hsz1273327/cognihub_pyeffectref.git
cd cognihub_pyeffectref
# 安装开发依赖
pip install -e ".[dev]"
```
### 运行测试
```bash
# 运行所有测试
pytest
# 运行测试并生成覆盖率报告
pytest --cov=cognihub_pyeffectref --cov-report=html
# 运行特定测试文件
pytest tests/test_ref.py -v
```
### 代码质量检查
```bash
# 格式化代码
black .
isort .
# 类型检查
mypy cognihub_pyeffectref
# 代码风格检查
flake8 cognihub_pyeffectref
```
### 项目结构
```
cognihub_pyeffectref/
├── cognihub_pyeffectref/ # 主要源码
│ ├── __init__.py # 公共接口导出
│ ├── ref.py # 底层接口:Ref, ReadOnlyRef
│ ├── effect.py # effect 装饰器和 EffectWrapper
│ ├── reactive_dict.py # 高级接口:ReactiveDict
│ └── local.py # 上下文管理(threading.local 等)
├── tests/ # 测试文件
│ ├── test_ref.py # Ref 相关测试
│ ├── test_effect.py # effect 相关测试
│ └── test_reactive_dict.py # ReactiveDict 相关测试
├── examples/ # 使用示例
└── docs/ # 文档
```
## 🤝 贡献指南
欢迎贡献代码!请遵循以下流程:
### 开发流程
1. **Fork 项目**并创建功能分支
2. **编写代码**并确保测试通过
3. **添加测试**覆盖新功能
4. **更新文档**如有必要
5. **提交 Pull Request**
### 代码规范
- 使用 **类型提示** (`typing` 模块)
- 遵循 **PEP 8** 代码风格
- 编写 **docstring** 描述函数和类
- 保持 **测试覆盖率** > 90%
- 确保所有测试通过
### 提交信息规范
使用语义化提交信息:
```bash
feat: 添加新功能
fix: 修复 bug
docs: 更新文档
test: 添加测试
refactor: 重构代码
style: 代码格式调整
```
### Issue 和 PR 模板
- **Bug 报告**: 提供复现步骤、预期行为、实际行为
- **功能请求**: 描述用例、期望 API、实现建议
- **Pull Request**: 关联 Issue、说明变更、添加测试
## 📄 许可证
本项目采用 MIT 许可证.详见 [LICENSE](LICENSE) 文件.
## 📚 相关资源
- **GitHub**: [cognihub_pyeffectref](https://github.com/hsz1273327/cognihub_pyeffectref)
- **PyPI**: [cognihub-pyeffectref](https://pypi.org/project/cognihub-pyeffectref/)
- **示例**: [examples/](https://github.com/hsz1273327/cognihub_pyeffectref/tree/master/examples)
## 🔄 变更日志
查看 [CHANGELOG.md](CHANGELOG.md) 了解完整变更历史.
---
**感谢使用 CogniHub PyEffectRef!** 🚀
如有问题或建议,欢迎提交 [Issue](https://github.com/hsz1273327/cognihub_pyeffectref/issues) 或参与讨论.
Raw data
{
"_id": null,
"home_page": null,
"name": "cognihub-pyeffectref",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": "hsz1273327 <hsz1273327@163.com>",
"keywords": "reactive, vue, effect, ref, cognihub, reactivedict, readonly, async, threading",
"author": null,
"author_email": "hsz1273327 <hsz1273327@163.com>",
"download_url": "https://files.pythonhosted.org/packages/ac/a8/bc56eae86861b2c8bff93aed801b31ac99132c722be98a0d7664212747b4/cognihub_pyeffectref-0.2.0.tar.gz",
"platform": null,
"description": "# CogniHub PyEffectRef\n\nCogniHub \u9879\u76ee\u7684\u54cd\u5e94\u5f0f\u7f16\u7a0b\u5e93,\u63d0\u4f9b\u7c7b\u4f3c Vue 3 Composition API \u7684\u54cd\u5e94\u5f0f\u7cfb\u7edf.\u672c\u5e93\u5206\u4e3a**\u5e95\u5c42\u63a5\u53e3**\u548c**\u9ad8\u7ea7\u63a5\u53e3**\u4e24\u4e2a\u5c42\u6b21,\u6ee1\u8db3\u4e0d\u540c\u590d\u6742\u5ea6\u7684\u4f7f\u7528\u9700\u6c42.\n\n## \u7279\u6027\n\n- \ud83d\udd04 **\u54cd\u5e94\u5f0f\u7f16\u7a0b**: \u7c7b\u4f3c Vue 3 Composition API \u7684\u54cd\u5e94\u5f0f\u7cfb\u7edf\n- \ud83d\udd12 **\u7ebf\u7a0b\u5b89\u5168**: \u652f\u6301\u591a\u7ebf\u7a0b\u73af\u5883\u4e0b\u7684\u5b89\u5168\u64cd\u4f5c \n- \u26a1 **\u5f02\u6b65\u652f\u6301**: \u5b8c\u6574\u652f\u6301 asyncio \u534f\u7a0b\u73af\u5883\n- \ud83c\udfaf **\u7c7b\u578b\u63d0\u793a**: \u5b8c\u6574\u7684 TypeScript \u98ce\u683c\u7c7b\u578b\u63d0\u793a\u652f\u6301\n- \ud83c\udfd7\ufe0f **\u5206\u5c42\u8bbe\u8ba1**: \u5e95\u5c42\u63a5\u53e3(Ref/effect) + \u9ad8\u7ea7\u63a5\u53e3(ReactiveDict/ReadOnlyView)\n- \ud83c\udf9b\ufe0f **\u6267\u884c\u63a7\u5236**: \u652f\u6301\u540c\u6b65\u3001\u5f02\u6b65\u3001\u987a\u5e8f\u6267\u884c\u7b49\u591a\u79cd\u6a21\u5f0f\n\n## \u5b89\u88c5\n\n```bash\npip install cognihub-pyeffectref\n```\n\n\u6216\u4ece\u6e90\u7801\u5b89\u88c5\uff1a\n\n```bash\ngit clone https://github.com/hsz1273327/cognihub_pyeffectref.git\ncd cognihub_pyeffectref\npip install -e .\n```\n\n## \ud83d\udcda \u67b6\u6784\u6982\u89c8\n\n\u672c\u5e93\u91c7\u7528\u5206\u5c42\u8bbe\u8ba1,\u63d0\u4f9b\u4e24\u4e2a\u5c42\u6b21\u7684\u63a5\u53e3\uff1a\n\n### \ud83d\udd27 \u5e95\u5c42\u63a5\u53e3 (Low-level APIs)\n\n- **`Ref[T]`**: \u54cd\u5e94\u5f0f\u6570\u636e\u5bb9\u5668,\u652f\u6301\u6cdb\u578b\u7c7b\u578b\u6307\u5b9a\n- **`effect`**: \u526f\u4f5c\u7528\u88c5\u9970\u5668,\u81ea\u52a8\u8ffd\u8e2a\u4f9d\u8d56\u5173\u7cfb\n- **`ReadOnlyRef[T]`**: \u53ea\u8bfb\u54cd\u5e94\u5f0f\u5f15\u7528\n\n**\u7279\u70b9**: \u76f4\u63a5\u4f7f\u7528\u6cdb\u578b\u6307\u5b9a\u7c7b\u578b,\u9002\u5408\u7b80\u5355\u6570\u636e\u7ed3\u6784\u548c\u6027\u80fd\u654f\u611f\u573a\u666f\n\n### \ud83c\udfd7\ufe0f \u9ad8\u7ea7\u63a5\u53e3 (High-level APIs)\n\n- **`ReactiveDict`**: \u54cd\u5e94\u5f0f\u5b57\u5178,\u652f\u6301\u5d4c\u5957\u7ed3\u6784,\u914d\u5408 TypedDict \u548c cast \u4f7f\u7528\n- **`ReadOnlyView`**: \u53ea\u8bfb\u89c6\u56fe,\u914d\u5408 Protocol \u548c cast \u4f7f\u7528\n\n**\u7279\u70b9**: \u9700\u8981\u7ed3\u5408 `TypedDict` \u548c `Protocol` \u6307\u5b9a\u590d\u6742\u7c7b\u578b\u7ed3\u6784,\u9002\u5408\u590d\u6742\u5e94\u7528\u573a\u666f\n\n## \ud83d\ude80 \u5feb\u901f\u5f00\u59cb\n\n### 1\ufe0f\u20e3 \u5e95\u5c42\u63a5\u53e3\u793a\u4f8b\n\n#### \u57fa\u672c\u7528\u6cd5 - \u6cdb\u578b\u7c7b\u578b\u6307\u5b9a\n\n```python\nfrom cognihub_pyeffectref import Ref, effect\nfrom typing import List, Dict\n\n# \u4f7f\u7528\u6cdb\u578b\u6307\u5b9a\u7c7b\u578b\ncount: Ref[int] = Ref(0)\nname: Ref[str] = Ref(\"Alice\") \nitems: Ref[List[str]] = Ref([\"apple\", \"banana\"])\n\n# \u521b\u5efa\u526f\u4f5c\u7528\u51fd\u6570\n@effect\ndef log_count() -> None:\n print(f\"Count is: {count.value}\")\n\n@effect \ndef log_greeting() -> None:\n print(f\"Hello, {name.value}!\")\n\n# \u521d\u59cb\u6267\u884c\nlog_count() # \u8f93\u51fa: Count is: 0\nlog_greeting() # \u8f93\u51fa: Hello, Alice!\n\n# \u4fee\u6539\u6570\u636e,\u81ea\u52a8\u89e6\u53d1\u526f\u4f5c\u7528\ncount.value = 5 # \u8f93\u51fa: Count is: 5\nname.value = \"Bob\" # \u8f93\u51fa: Hello, Bob!\n```\n\n#### \u540c\u6b65/\u5f02\u6b65/\u591a\u7ebf\u7a0b\u652f\u6301\n\n\u9ed8\u8ba4\u5355\u7ebf\u7a0b\u540c\u6b65\u6267\u884c,\u901a\u8fc7`Ref.configure_sync_task_executor`\u53ef\u914d\u7f6e\u4e3a\u5f02\u6b65\u6216\u591a\u7ebf\u7a0b\u6267\u884c.\n\n**\u6ce8\u610f:**\n\n1. \u8bf7\u4ec5\u5728\u9700\u8981\u65f6\u4f7f\u7528\u5f02\u6b65\u6216\u591a\u7ebf\u7a0b,\u56e0\u4e3a\u8fd9\u4f1a\u589e\u52a0\u590d\u6742\u6027\u548c\u8c03\u8bd5\u96be\u5ea6.\n2. \u4ec5\u5728\u5e94\u7528\u5165\u53e3\u5904\u8bbe\u7f6e\u4e00\u6b21\u6267\u884c\u5668\u914d\u7f6e\u5373\u53ef,\u4e0d\u9700\u8981\u5728\u6bcf\u4e2a\u6a21\u5757\u4e2d\u91cd\u590d\u8bbe\u7f6e.\u8bbe\u7f6e\u63a5\u53e3\u662f\u4e00\u6b21\u6027\u7684,\u4e00\u65e6\u8bbe\u7f6e\u8fc7\u4e86\u5c31\u65e0\u6cd5\u6539\u53d8\n\n```python\nimport asyncio\nimport threading\nimport time\nfrom cognihub_pyeffectref import Ref, effect\n\ndata: Ref[str] = Ref(\"initial\")\n\n# 1. \u540c\u6b65\u4f7f\u7528\n@effect\ndef sync_effect() -> None:\n print(f\"Sync effect: {data.value}\")\n\n# 2. \u5f02\u6b65\u4f7f\u7528 \n@effect\nasync def async_effect() -> None:\n print(f\"Async effect: {data.value}\")\n await asyncio.sleep(0.1) # \u5f02\u6b65\u64cd\u4f5c\n\n# 3. \u591a\u7ebf\u7a0b\u4f7f\u7528\ndef thread_worker(thread_id: int) -> None:\n @effect\n def thread_effect() -> None:\n print(f\"Thread {thread_id} effect: {data.value}\")\n \n thread_effect() # \u5efa\u7acb\u4f9d\u8d56\n time.sleep(0.1)\n\nasync def main():\n # \u540c\u6b65\u6267\u884c\n sync_effect()\n \n # \u5f02\u6b65\u6267\u884c \n await async_effect()\n \n # \u591a\u7ebf\u7a0b\u6267\u884c\n threads = []\n for i in range(3):\n thread = threading.Thread(target=thread_worker, args=(i,))\n thread.start()\n threads.append(thread)\n \n # \u89e6\u53d1\u6240\u6709\u526f\u4f5c\u7528\n data.value = \"updated\"\n await asyncio.sleep(0.2) # \u7b49\u5f85\u5f02\u6b65\u548c\u7ebf\u7a0b\u5b8c\u6210\n \n for thread in threads:\n thread.join()\n\n# asyncio.run(main())\n```\n\n#### \u6267\u884c\u5668\u914d\u7f6e - \u63a7\u5236\u56de\u8c03\u6267\u884c\u65b9\u5f0f\n\n> \u9ed8\u8ba4\u7684\u56de\u8c03\u6267\u884c\u65b9\u5f0f:\n\n1. \u5728\u672a\u8bbe\u7f6e\u6267\u884c\u5668\u65f6\n 1. \u540c\u6b65\u56de\u8c03\u5728\u5f53\u524d\u7ebf\u7a0b\u540c\u6b65\u6267\u884c.\n 2. \u5f02\u6b65\u56de\u8c03\u5728 asyncio \u4e8b\u4ef6\u5faa\u73af\u4e2d\u5f02\u6b65\u6267\u884c.\n\n2. \u5728\u8bbe\u7f6e\u4e86\u6267\u884c\u5668\u7684\u60c5\u51b5\u4e0b\n 1. \u540c\u6b65\u56de\u8c03\u4f1a\u5728\u6267\u884c\u5668\u4e2d\u591a\u7ebf\u7a0b\u540e\u53f0\u6267\u884c.\n 2. \u5f02\u6b65\u56de\u8c03\u4f1a\u5728 asyncio \u4e8b\u4ef6\u5faa\u73af\u4e2d\u5f02\u6b65\u6267\u884c.\n\n> \u5b9e\u4f8b\u7684\u7cbe\u7ec6\u5316\u56de\u8c03\u6267\u884c\u63a7\u5236\n\n\u5b9e\u4f8b\u53ef\u4ee5\u5728\u521d\u59cb\u5316\u65f6\u989d\u5916\u6307\u5b9a\u5176\u7528`subscribe`\u6ce8\u518c\u7684\u540c\u6b65\u56de\u8c03\u51fd\u6570\u7684\u6267\u884c\u65b9\u5f0f.\n\n1. `subscribe_immediate=True`: \u5f3a\u5236\u5728\u5f53\u524d\u7ebf\u7a0b\u540c\u6b65\u6267\u884c\u56de\u8c03,\u5ffd\u7565\u5168\u5c40\u6267\u884c\u5668\u914d\u7f6e.\n2. `subscribe_sequential=True`: \u4fdd\u8bc1\u540c\u6b65\u56de\u8c03\u51fd\u6570\u6309\u6ce8\u518c\u987a\u5e8f\u5728\u540e\u53f0\u6267\u884c,\u9002\u7528\u4e8e\u9700\u8981\u987a\u5e8f\u6267\u884c\u7684\u573a\u666f.\n\n```python\nimport concurrent.futures\nfrom cognihub_pyeffectref import Ref\n\n# \u914d\u7f6e\u5168\u5c40\u6267\u884c\u5668 - \u4f7f\u7528 asyncio \u7ebf\u7a0b\u6c60\nRef.configure_sync_task_executor('asyncio')\n\n# \u6216\u4f7f\u7528\u81ea\u5b9a\u4e49\u7ebf\u7a0b\u6c60\nexecutor = concurrent.futures.ThreadPoolExecutor(max_workers=4)\nRef.configure_sync_task_executor(executor)\n\n# \u5f3a\u5236\u7acb\u5373\u540c\u6b65\u6267\u884c (\u5ffd\u7565\u6267\u884c\u5668\u914d\u7f6e)\nimmediate_ref = Ref(0, subscribe_immediate=True)\n\n# \u4fdd\u8bc1\u987a\u5e8f\u6267\u884c (\u5728\u6267\u884c\u5668\u4e2d\u6309\u987a\u5e8f\u6267\u884c)\nsequential_ref = Ref(0, subscribe_sequential=True)\n```\n\n### 2\ufe0f\u20e3 \u9ad8\u7ea7\u63a5\u53e3\u793a\u4f8b\n\n#### ReactiveDict - \u7ed3\u5408 TypedDict \u6307\u5b9a\u7ed3\u6784\n\n```python\nfrom cognihub_pyeffectref import ReactiveDict, effect\nfrom typing import TypedDict, cast\n\n# 1. \u5b9a\u4e49\u6570\u636e\u7ed3\u6784\nclass UserData(TypedDict):\n name: str\n email: str \n age: int\n is_active: bool\n\n# 2. \u521b\u5efa\u54cd\u5e94\u5f0f\u5b57\u5178\nuser_dict = ReactiveDict({\n 'name': 'Alice',\n 'email': 'alice@example.com', \n 'age': 25,\n 'is_active': True\n})\n\n# 3. \u7c7b\u578b\u8f6c\u6362\u4ee5\u83b7\u5f97\u7c7b\u578b\u63d0\u793a\nuser: UserData = cast(UserData, user_dict)\n\n# 4. \u4f7f\u7528\u65f6\u4eab\u53d7\u5b8c\u6574\u7c7b\u578b\u63d0\u793a\n@effect\ndef watch_user() -> None:\n print(f\"User: {user['name']} ({user['age']})\")\n print(f\"Email: {user['email']}\")\n print(f\"Active: {user['is_active']}\")\n\nwatch_user()\n\n# 5. \u4fee\u6539\u6570\u636e\nuser['name'] = 'Bob' # \u81ea\u52a8\u89e6\u53d1\u526f\u4f5c\u7528\nuser['age'] = 26 # \u81ea\u52a8\u89e6\u53d1\u526f\u4f5c\u7528\n```\n\n#### ReadOnlyView - \u7ed3\u5408 Protocol \u521b\u5efa\u53ea\u8bfb\u89c6\u56fe\n\n```python\nfrom cognihub_pyeffectref import ReactiveDict, ReadOnlyView, ReadOnlyRef, effect\nfrom typing import Protocol, cast, Any\n\n# 1. \u5b9a\u4e49 Protocol \u63cf\u8ff0\u53ea\u8bfb\u89c6\u56fe\u7ed3\u6784\nclass UserViewProtocol(Protocol):\n name: ReadOnlyRef[str]\n email: ReadOnlyRef[str] \n age: ReadOnlyRef[int]\n is_active: ReadOnlyRef[bool]\n \n def __call__(self) -> dict[str, Any]: ...\n\n# 2. \u521b\u5efa\u539f\u59cb\u6570\u636e\nuser_data = ReactiveDict({\n 'name': 'Alice',\n 'email': 'alice@example.com',\n 'age': 25, \n 'is_active': True\n})\n\n# 3. \u521b\u5efa\u53ea\u8bfb\u89c6\u56fe\nuser_view = cast(UserViewProtocol, ReadOnlyView(user_data))\n\n# 4. \u53ea\u8bfb\u8bbf\u95ee - \u4eab\u53d7\u5b8c\u6574\u7c7b\u578b\u63d0\u793a\u548c\u9632\u62a4\n@effect \ndef watch_user_view() -> None:\n print(f\"Name: {user_view.name.value}\")\n print(f\"Email: {user_view.email.value}\")\n print(f\"Age: {user_view.age.value}\")\n\nwatch_user_view()\n\n# 5. \u65e0\u6cd5\u4fee\u6539 (\u7f16\u8bd1\u65f6\u548c\u8fd0\u884c\u65f6\u90fd\u4f1a\u62a5\u9519)\n# user_view.name.value = \"Bob\" # AttributeError: ReadOnlyRef has no setter\n# user_view['name'] = \"Bob\" # TypeError: ReadOnlyView is not subscriptable\n\n# 6. \u53ea\u80fd\u901a\u8fc7\u539f\u59cb\u6570\u636e\u4fee\u6539\nuser_data.name = \"Bob\" # \u81ea\u52a8\u89e6\u53d1\u53ea\u8bfb\u89c6\u56fe\u7684\u526f\u4f5c\u7528\n```\n\n#### \u590d\u6742\u5d4c\u5957\u6570\u636e\u7ed3\u6784\n\n```python\nfrom cognihub_pyeffectref import ReactiveDict, ReadOnlyView, ReadOnlyRef, effect\nfrom typing import Protocol, cast, Any\n\n# 1. \u5b9a\u4e49\u5d4c\u5957\u7684 Protocol \u7ed3\u6784\nclass DatabaseConfig(Protocol):\n host: ReadOnlyRef[str]\n port: ReadOnlyRef[int]\n name: ReadOnlyRef[str]\n\nclass ApiConfig(Protocol): \n base_url: ReadOnlyRef[str]\n timeout: ReadOnlyRef[int]\n retry_count: ReadOnlyRef[int]\n\nclass AppConfig(Protocol):\n database: DatabaseConfig\n api: ApiConfig\n debug_mode: ReadOnlyRef[bool]\n \n def __call__(self) -> dict[str, Any]: ...\n\n# 2. \u521b\u5efa\u5d4c\u5957\u6570\u636e\nconfig_data = ReactiveDict({\n 'database': {\n 'host': 'localhost',\n 'port': 5432,\n 'name': 'myapp'\n },\n 'api': {\n 'base_url': 'https://api.example.com',\n 'timeout': 30,\n 'retry_count': 3\n },\n 'debug_mode': False\n})\n\n# 3. \u521b\u5efa\u7c7b\u578b\u5316\u7684\u53ea\u8bfb\u89c6\u56fe\nconfig_view = cast(AppConfig, ReadOnlyView(config_data))\n\n# 4. \u8bbf\u95ee\u5d4c\u5957\u6570\u636e - \u5b8c\u6574\u7c7b\u578b\u63d0\u793a\n@effect\ndef watch_config() -> None:\n db_host = config_view.database.host.value\n api_url = config_view.api.base_url.value \n debug = config_view.debug_mode.value\n \n print(f\"Database: {db_host}\")\n print(f\"API: {api_url}\")\n print(f\"Debug: {debug}\")\n\nwatch_config()\n\n# 5. \u4fee\u6539\u539f\u59cb\u6570\u636e\u89e6\u53d1\u53d8\u66f4\nconfig_data.database.host = 'production-db'\nconfig_data.debug_mode = True\n```\n\n## \ud83d\udcd6 API \u53c2\u8003\n\n### \ud83d\udd27 \u5e95\u5c42\u63a5\u53e3 (Low-level APIs)\n\n#### Ref[T]\n\n\u54cd\u5e94\u5f0f\u6570\u636e\u5bb9\u5668\u7c7b,\u652f\u6301\u6cdb\u578b\u7c7b\u578b\u6307\u5b9a.\n\n**\u6784\u9020\u51fd\u6570**\n\n- `Ref(initial_value: T, subscribe_immediate: bool = False, subscribe_sequential: bool = False)`\n - `initial_value`: \u521d\u59cb\u503c\n - `subscribe_immediate`: \u662f\u5426\u5f3a\u5236\u5728\u5f53\u524d\u7ebf\u7a0b\u540c\u6b65\u6267\u884c\u56de\u8c03 (\u5ffd\u7565\u5168\u5c40\u6267\u884c\u5668\u914d\u7f6e)\n - `subscribe_sequential`: \u662f\u5426\u4fdd\u8bc1\u56de\u8c03\u6309\u6ce8\u518c\u987a\u5e8f\u6267\u884c\n\n**\u5c5e\u6027**\n\n- `value: T`: \u83b7\u53d6\u6216\u8bbe\u7f6e\u5f15\u7528\u7684\u503c\n\n**\u65b9\u6cd5**\n\n- `subscribe(callback: Callable[[T, T], None])`: \u8ba2\u9605\u503c\u53d8\u5316\n- `unsubscribe(callback: Callable[[T, T], None])`: \u53d6\u6d88\u8ba2\u9605\n- `configure_sync_task_executor(executor)`: \u914d\u7f6e\u5168\u5c40\u540c\u6b65\u4efb\u52a1\u6267\u884c\u5668\n\n**\u7c7b\u65b9\u6cd5**\n\n- `Ref.configure_sync_task_executor('asyncio' | ThreadPoolExecutor)`: \u914d\u7f6e\u5168\u5c40\u6267\u884c\u5668\n\n#### ReadOnlyRef[T] \n\n\u53ea\u8bfb\u54cd\u5e94\u5f0f\u5f15\u7528,\u4ece Ref \u521b\u5efa.\n\n**\u6784\u9020\u51fd\u6570**\n\n- `ReadOnlyRef(ref: Ref[T])`: \u4ece\u73b0\u6709 Ref \u521b\u5efa\u53ea\u8bfb\u5f15\u7528\n\n**\u5c5e\u6027**\n\n- `value: T`: \u53ea\u8bfb\u8bbf\u95ee\u5f15\u7528\u7684\u503c (\u65e0 setter)\n\n**\u65b9\u6cd5**\n\n- `subscribe(callback: Callable[[T, T], None])`: \u8ba2\u9605\u503c\u53d8\u5316\n- `unsubscribe(callback: Callable[[T, T], None])`: \u53d6\u6d88\u8ba2\u9605\n\n#### effect\n\n\u526f\u4f5c\u7528\u88c5\u9970\u5668,\u81ea\u52a8\u8ffd\u8e2a Ref \u4f9d\u8d56\u5173\u7cfb.\n\n```python\n@effect\ndef my_effect() -> None:\n # \u8bbf\u95ee Ref.value \u4f1a\u81ea\u52a8\u5efa\u7acb\u4f9d\u8d56\u5173\u7cfb\n pass\n\n# \u6216\u624b\u52a8\u8c03\u7528\neffect_wrapper = effect(my_function)\neffect_wrapper()\n```\n\n#### EffectWrapper\n\neffect \u88c5\u9970\u5668\u8fd4\u56de\u7684\u5305\u88c5\u5668\u7c7b.\n\n**\u65b9\u6cd5**\n\n- `stop()`: \u505c\u6b62\u526f\u4f5c\u7528,\u4f7f\u5176\u4e0d\u518d\u54cd\u5e94\u6570\u636e\u53d8\u5316\n\n### \ud83c\udfd7\ufe0f \u9ad8\u7ea7\u63a5\u53e3 (High-level APIs)\n\n#### ReactiveDict\n\n\u54cd\u5e94\u5f0f\u5b57\u5178,\u652f\u6301\u5d4c\u5957\u7ed3\u6784\u548c\u52a8\u6001\u952e.\n\n**\u6784\u9020\u51fd\u6570**\n\n- `ReactiveDict(initial_data: dict = None)`: \u521b\u5efa\u54cd\u5e94\u5f0f\u5b57\u5178\n\n**\u65b9\u6cd5**\n\n- `to_dict() -> dict`: \u8f6c\u6362\u4e3a\u666e\u901a\u5b57\u5178\n- `keys()`, `values()`, `items()`: \u5b57\u5178\u63a5\u53e3\u65b9\u6cd5\n- `get(key, default=None)`: \u83b7\u53d6\u503c\n- `pop(key, default=None)`: \u5220\u9664\u5e76\u8fd4\u56de\u503c\n- `clear()`: \u6e05\u7a7a\u5b57\u5178\n- `update(other)`: \u66f4\u65b0\u5b57\u5178\n\n**\u7279\u6027**\n\n- \u652f\u6301\u5d4c\u5957\u7ed3\u6784\u81ea\u52a8\u8f6c\u6362\u4e3a ReactiveDict\n- \u52a8\u6001\u5c5e\u6027\u8bbf\u95ee: `obj.key` \u7b49\u4ef7\u4e8e `obj['key']`\n- \u4e0e TypedDict \u7ed3\u5408\u4f7f\u7528\u83b7\u5f97\u7c7b\u578b\u63d0\u793a\n\n#### ReadOnlyView\n\n\u53ea\u8bfb\u89c6\u56fe,\u4ece ReactiveDict \u521b\u5efa\u7ed3\u6784\u5316\u7684\u53ea\u8bfb\u8bbf\u95ee.\n\n**\u6784\u9020\u51fd\u6570**\n\n- `ReadOnlyView(reactive_dict: ReactiveDict)`: \u521b\u5efa\u53ea\u8bfb\u89c6\u56fe\n\n**\u7279\u6027**\n\n- \u9012\u5f52\u5c06\u6240\u6709\u503c\u8f6c\u6362\u4e3a ReadOnlyRef\n- \u652f\u6301\u5d4c\u5957\u7ed3\u6784\u7684\u53ea\u8bfb\u8bbf\u95ee \n- \u4e0e Protocol \u7ed3\u5408\u83b7\u5f97\u5b8c\u6574\u7c7b\u578b\u63d0\u793a\n- \u8c03\u7528 `view()` \u8fd4\u56de\u5f53\u524d\u72b6\u6001\u7684\u5b57\u5178\u5feb\u7167\n\n**\u4f7f\u7528\u6a21\u5f0f**\n\n```python\n# 1. \u5b9a\u4e49\u7ed3\u6784 Protocol\nclass MyDataProtocol(Protocol):\n field: ReadOnlyRef[str]\n def __call__(self) -> dict[str, Any]: ...\n\n# 2. \u521b\u5efa\u5e76\u8f6c\u6362\ndata = ReactiveDict({'field': 'value'})\nview = cast(MyDataProtocol, ReadOnlyView(data))\n\n# 3. \u7c7b\u578b\u5b89\u5168\u8bbf\u95ee\nprint(view.field.value) # \u5b8c\u6574\u7c7b\u578b\u63d0\u793a\nsnapshot = view() # \u83b7\u53d6\u5f53\u524d\u72b6\u6001\u5feb\u7167\n```\n\n## \u26a1 \u6267\u884c\u6a21\u5f0f\u8be6\u89e3\n\n### \u540c\u6b65\u6267\u884c (\u9ed8\u8ba4)\n\n```python\nfrom cognihub_pyeffectref import Ref, effect\n\n# \u9ed8\u8ba4\u6a21\u5f0f\uff1a\u56de\u8c03\u5728\u5f53\u524d\u7ebf\u7a0b\u4e2d\u6267\u884c\ndata = Ref(\"initial\")\n\n@effect\ndef sync_effect() -> None:\n print(f\"Current value: {data.value}\")\n\nsync_effect() # \u7acb\u5373\u6267\u884c\ndata.value = \"changed\" # \u5728\u5f53\u524d\u7ebf\u7a0b\u4e2d\u89e6\u53d1\u56de\u8c03\n```\n\n### \u5f02\u6b65\u6267\u884c (asyncio \u73af\u5883)\n\n```python\nimport asyncio\nfrom cognihub_pyeffectref import Ref, effect\n\n# \u914d\u7f6e\u4f7f\u7528 asyncio \u7ebf\u7a0b\u6c60\nRef.configure_sync_task_executor('asyncio')\n\ndata = Ref(\"initial\")\n\n@effect\nasync def async_effect() -> None:\n print(f\"Async value: {data.value}\")\n await asyncio.sleep(0.1)\n\nasync def main():\n await async_effect() # \u5efa\u7acb\u4f9d\u8d56\n data.value = \"changed\" # \u56de\u8c03\u5c06\u5728 asyncio.to_thread \u4e2d\u6267\u884c\n await asyncio.sleep(0.2) # \u7b49\u5f85\u5f02\u6b65\u56de\u8c03\u5b8c\u6210\n\nasyncio.run(main())\n```\n\n### \u591a\u7ebf\u7a0b\u6267\u884c\n\n```python\nimport threading\nimport concurrent.futures\nfrom cognihub_pyeffectref import Ref, effect\n\n# \u914d\u7f6e\u81ea\u5b9a\u4e49\u7ebf\u7a0b\u6c60\nexecutor = concurrent.futures.ThreadPoolExecutor(max_workers=4)\nRef.configure_sync_task_executor(executor)\n\ndata = Ref(0)\n\n@effect\ndef thread_effect() -> None:\n thread_id = threading.get_ident()\n value = data.value\n print(f\"Thread {thread_id}: {value}\")\n\n# \u5728\u591a\u4e2a\u7ebf\u7a0b\u4e2d\u5efa\u7acb\u4f9d\u8d56\nthreads = []\nfor i in range(3):\n thread = threading.Thread(target=thread_effect)\n thread.start()\n threads.append(thread)\n\n# \u89e6\u53d1\u56de\u8c03 - \u5c06\u5728\u7ebf\u7a0b\u6c60\u4e2d\u6267\u884c\ndata.value = 42\n\nfor thread in threads:\n thread.join()\n \nexecutor.shutdown(wait=True)\n```\n\n### \u6267\u884c\u63a7\u5236\u9009\u9879\n\n```python\nfrom cognihub_pyeffectref import Ref\n\n# 1. \u5f3a\u5236\u7acb\u5373\u540c\u6b65\u6267\u884c (\u5ffd\u7565\u5168\u5c40\u6267\u884c\u5668)\nimmediate_ref = Ref(0, subscribe_immediate=True)\n\n# 2. \u4fdd\u8bc1\u987a\u5e8f\u6267\u884c (\u5728\u6267\u884c\u5668\u4e2d\u6309\u6ce8\u518c\u987a\u5e8f\u6267\u884c) \nsequential_ref = Ref(0, subscribe_sequential=True)\n\n# 3. \u7ec4\u5408\u4f7f\u7528 (immediate \u4f18\u5148\u7ea7\u66f4\u9ad8)\ncombined_ref = Ref(0, subscribe_immediate=True, subscribe_sequential=True)\n```\n\n## \ud83d\udd12 \u7ebf\u7a0b\u5b89\u5168\u7279\u6027\n\n\u672c\u5e93\u5728\u8bbe\u8ba1\u65f6\u5145\u5206\u8003\u8651\u4e86\u7ebf\u7a0b\u5b89\u5168\uff1a\n\n### \u5185\u90e8\u9501\u673a\u5236\n\n- `Ref` \u4f7f\u7528\u5185\u90e8\u9501\u4fdd\u62a4\u8ba2\u9605\u8005\u96c6\u5408\u7684\u5e76\u53d1\u4fee\u6539\n- \u652f\u6301\u5728\u591a\u7ebf\u7a0b\u73af\u5883\u4e2d\u5b89\u5168\u5730\u8bfb\u5199\u54cd\u5e94\u5f0f\u6570\u636e\n- `ReactiveDict` \u7684\u5d4c\u5957\u64cd\u4f5c\u4e5f\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\n\n### \u4e0a\u4e0b\u6587\u9694\u79bb\n\n- \u5f02\u6b65\u73af\u5883\u4f7f\u7528 `contextvars` \u9694\u79bb effect \u4e0a\u4e0b\u6587\n- \u591a\u7ebf\u7a0b\u73af\u5883\u4f7f\u7528 `threading.local` \u8fdb\u884c\u7ebf\u7a0b\u9694\u79bb\n- \u786e\u4fdd\u526f\u4f5c\u7528\u51fd\u6570\u5728\u6b63\u786e\u7684\u4e0a\u4e0b\u6587\u4e2d\u6267\u884c\n\n### \u6267\u884c\u5668\u914d\u7f6e\n\n- \u5168\u5c40\u6267\u884c\u5668\u914d\u7f6e\u652f\u6301 `'asyncio'` \u548c\u81ea\u5b9a\u4e49 `ThreadPoolExecutor`\n- `subscribe_immediate=True` \u53ef\u5f3a\u5236\u5728\u5f53\u524d\u7ebf\u7a0b\u540c\u6b65\u6267\u884c,\u63d0\u4f9b\u786e\u5b9a\u6027\u884c\u4e3a\n- `subscribe_sequential=True` \u4fdd\u8bc1\u56de\u8c03\u6309\u6ce8\u518c\u987a\u5e8f\u6267\u884c,\u907f\u514d\u7ade\u6001\u6761\u4ef6\n\n## \ud83c\udfaf \u4f7f\u7528\u573a\u666f\u63a8\u8350\n\n### \u4f55\u65f6\u4f7f\u7528\u5e95\u5c42\u63a5\u53e3 (Ref/effect)\n\n\u2705 **\u9002\u5408\u573a\u666f**:\n\n- \u7b80\u5355\u7684\u54cd\u5e94\u5f0f\u72b6\u6001\u7ba1\u7406\n- \u6027\u80fd\u654f\u611f\u7684\u5e94\u7528\n- \u9700\u8981\u7cbe\u786e\u63a7\u5236\u6267\u884c\u65f6\u673a\n- \u4e0e\u73b0\u6709\u4ee3\u7801\u96c6\u6210\n\n```python\n# \u9002\u5408\uff1a\u7b80\u5355\u8ba1\u6570\u5668\ncounter: Ref[int] = Ref(0)\nuser_name: Ref[str] = Ref(\"Anonymous\")\n```\n\n### \u4f55\u65f6\u4f7f\u7528\u9ad8\u7ea7\u63a5\u53e3 (ReactiveDict/ReadOnlyView)\n\n\u2705 **\u9002\u5408\u573a\u666f**:\n\n- \u590d\u6742\u7684\u5d4c\u5957\u6570\u636e\u7ed3\u6784\n- \u9700\u8981\u7ed3\u6784\u5316\u7684\u6570\u636e\u8bbf\u95ee\n- \u914d\u7f6e\u7ba1\u7406\u7cfb\u7edf\n- \u5927\u578b\u5e94\u7528\u7684\u72b6\u6001\u7ba1\u7406\n\n```python\n# \u9002\u5408\uff1a\u590d\u6742\u914d\u7f6e\u5bf9\u8c61\napp_config = ReactiveDict({\n 'database': {'host': 'localhost', 'port': 5432},\n 'cache': {'ttl': 3600, 'max_size': 1000},\n 'features': {'debug': False, 'analytics': True}\n})\n```\n\n### \u6df7\u5408\u4f7f\u7528\u6a21\u5f0f\n\n```python\n# \u7ec4\u5408\u4f7f\u7528\uff1a\u5e95\u5c42\u63a5\u53e3\u5904\u7406\u6838\u5fc3\u72b6\u6001,\u9ad8\u7ea7\u63a5\u53e3\u7ba1\u7406\u914d\u7f6e\nfrom cognihub_pyeffectref import Ref, ReactiveDict, ReadOnlyView, effect\nfrom typing import Protocol, cast\n\n# \u5e95\u5c42\uff1a\u6838\u5fc3\u5e94\u7528\u72b6\u6001\napp_state: Ref[str] = Ref(\"initializing\")\nuser_count: Ref[int] = Ref(0)\n\n# \u9ad8\u7ea7\uff1a\u590d\u6742\u914d\u7f6e\u7ba1\u7406\nconfig_data = ReactiveDict({\n 'ui': {'theme': 'dark', 'language': 'en'},\n 'api': {'timeout': 30, 'retries': 3}\n})\n\nclass ConfigProtocol(Protocol):\n ui: dict[str, str]\n api: dict[str, int]\n\nconfig = cast(ConfigProtocol, config_data)\n\n@effect\ndef sync_state() -> None:\n state = app_state.value\n theme = config['ui']['theme']\n print(f\"App {state} with {theme} theme\")\n\nsync_state()\n```\n\n## \ud83d\udee0\ufe0f \u5f00\u53d1\u6307\u5357\n\n### \u73af\u5883\u8bbe\u7f6e\n\n```bash\n# \u514b\u9686\u4ed3\u5e93\ngit clone https://github.com/hsz1273327/cognihub_pyeffectref.git\ncd cognihub_pyeffectref\n\n# \u5b89\u88c5\u5f00\u53d1\u4f9d\u8d56\npip install -e \".[dev]\"\n```\n\n### \u8fd0\u884c\u6d4b\u8bd5\n\n```bash\n# \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\npytest\n\n# \u8fd0\u884c\u6d4b\u8bd5\u5e76\u751f\u6210\u8986\u76d6\u7387\u62a5\u544a\npytest --cov=cognihub_pyeffectref --cov-report=html\n\n# \u8fd0\u884c\u7279\u5b9a\u6d4b\u8bd5\u6587\u4ef6\npytest tests/test_ref.py -v\n```\n\n### \u4ee3\u7801\u8d28\u91cf\u68c0\u67e5\n\n```bash\n# \u683c\u5f0f\u5316\u4ee3\u7801\nblack .\nisort .\n\n# \u7c7b\u578b\u68c0\u67e5\nmypy cognihub_pyeffectref\n\n# \u4ee3\u7801\u98ce\u683c\u68c0\u67e5\nflake8 cognihub_pyeffectref\n```\n\n### \u9879\u76ee\u7ed3\u6784\n\n```\ncognihub_pyeffectref/\n\u251c\u2500\u2500 cognihub_pyeffectref/ # \u4e3b\u8981\u6e90\u7801\n\u2502 \u251c\u2500\u2500 __init__.py # \u516c\u5171\u63a5\u53e3\u5bfc\u51fa\n\u2502 \u251c\u2500\u2500 ref.py # \u5e95\u5c42\u63a5\u53e3\uff1aRef, ReadOnlyRef\n\u2502 \u251c\u2500\u2500 effect.py # effect \u88c5\u9970\u5668\u548c EffectWrapper\n\u2502 \u251c\u2500\u2500 reactive_dict.py # \u9ad8\u7ea7\u63a5\u53e3\uff1aReactiveDict\n\u2502 \u2514\u2500\u2500 local.py # \u4e0a\u4e0b\u6587\u7ba1\u7406\uff08threading.local \u7b49\uff09\n\u251c\u2500\u2500 tests/ # \u6d4b\u8bd5\u6587\u4ef6\n\u2502 \u251c\u2500\u2500 test_ref.py # Ref \u76f8\u5173\u6d4b\u8bd5\n\u2502 \u251c\u2500\u2500 test_effect.py # effect \u76f8\u5173\u6d4b\u8bd5\n\u2502 \u2514\u2500\u2500 test_reactive_dict.py # ReactiveDict \u76f8\u5173\u6d4b\u8bd5\n\u251c\u2500\u2500 examples/ # \u4f7f\u7528\u793a\u4f8b\n\u2514\u2500\u2500 docs/ # \u6587\u6863\n```\n\n## \ud83e\udd1d \u8d21\u732e\u6307\u5357\n\n\u6b22\u8fce\u8d21\u732e\u4ee3\u7801\uff01\u8bf7\u9075\u5faa\u4ee5\u4e0b\u6d41\u7a0b\uff1a\n\n### \u5f00\u53d1\u6d41\u7a0b\n\n1. **Fork \u9879\u76ee**\u5e76\u521b\u5efa\u529f\u80fd\u5206\u652f\n2. **\u7f16\u5199\u4ee3\u7801**\u5e76\u786e\u4fdd\u6d4b\u8bd5\u901a\u8fc7\n3. **\u6dfb\u52a0\u6d4b\u8bd5**\u8986\u76d6\u65b0\u529f\u80fd\n4. **\u66f4\u65b0\u6587\u6863**\u5982\u6709\u5fc5\u8981\n5. **\u63d0\u4ea4 Pull Request**\n\n### \u4ee3\u7801\u89c4\u8303\n\n- \u4f7f\u7528 **\u7c7b\u578b\u63d0\u793a** (`typing` \u6a21\u5757)\n- \u9075\u5faa **PEP 8** \u4ee3\u7801\u98ce\u683c\n- \u7f16\u5199 **docstring** \u63cf\u8ff0\u51fd\u6570\u548c\u7c7b\n- \u4fdd\u6301 **\u6d4b\u8bd5\u8986\u76d6\u7387** > 90%\n- \u786e\u4fdd\u6240\u6709\u6d4b\u8bd5\u901a\u8fc7\n\n### \u63d0\u4ea4\u4fe1\u606f\u89c4\u8303\n\n\u4f7f\u7528\u8bed\u4e49\u5316\u63d0\u4ea4\u4fe1\u606f\uff1a\n\n```bash\nfeat: \u6dfb\u52a0\u65b0\u529f\u80fd\nfix: \u4fee\u590d bug \ndocs: \u66f4\u65b0\u6587\u6863\ntest: \u6dfb\u52a0\u6d4b\u8bd5\nrefactor: \u91cd\u6784\u4ee3\u7801\nstyle: \u4ee3\u7801\u683c\u5f0f\u8c03\u6574\n```\n\n### Issue \u548c PR \u6a21\u677f\n\n- **Bug \u62a5\u544a**: \u63d0\u4f9b\u590d\u73b0\u6b65\u9aa4\u3001\u9884\u671f\u884c\u4e3a\u3001\u5b9e\u9645\u884c\u4e3a\n- **\u529f\u80fd\u8bf7\u6c42**: \u63cf\u8ff0\u7528\u4f8b\u3001\u671f\u671b API\u3001\u5b9e\u73b0\u5efa\u8bae\n- **Pull Request**: \u5173\u8054 Issue\u3001\u8bf4\u660e\u53d8\u66f4\u3001\u6dfb\u52a0\u6d4b\u8bd5\n\n## \ud83d\udcc4 \u8bb8\u53ef\u8bc1\n\n\u672c\u9879\u76ee\u91c7\u7528 MIT \u8bb8\u53ef\u8bc1.\u8be6\u89c1 [LICENSE](LICENSE) \u6587\u4ef6.\n\n## \ud83d\udcda \u76f8\u5173\u8d44\u6e90\n\n- **GitHub**: [cognihub_pyeffectref](https://github.com/hsz1273327/cognihub_pyeffectref)\n- **PyPI**: [cognihub-pyeffectref](https://pypi.org/project/cognihub-pyeffectref/)\n- **\u793a\u4f8b**: [examples/](https://github.com/hsz1273327/cognihub_pyeffectref/tree/master/examples)\n\n## \ud83d\udd04 \u53d8\u66f4\u65e5\u5fd7\n\n\u67e5\u770b [CHANGELOG.md](CHANGELOG.md) \u4e86\u89e3\u5b8c\u6574\u53d8\u66f4\u5386\u53f2.\n\n---\n\n**\u611f\u8c22\u4f7f\u7528 CogniHub PyEffectRef\uff01** \ud83d\ude80\n\n\u5982\u6709\u95ee\u9898\u6216\u5efa\u8bae,\u6b22\u8fce\u63d0\u4ea4 [Issue](https://github.com/hsz1273327/cognihub_pyeffectref/issues) \u6216\u53c2\u4e0e\u8ba8\u8bba.\n",
"bugtrack_url": null,
"license": "MIT License\n \n Copyright (c) 2025 HUANG SIZHE\n \n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n \n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n \n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n ",
"summary": "A reactive programming library inspired by Vue 3's Composition API, featuring low-level (Ref/effect) and high-level (ReactiveDict/ReadOnlyView) interfaces",
"version": "0.2.0",
"project_urls": {
"Homepage": "https://github.com/hsz1273327/cognihub_pyeffectref",
"Issues": "https://github.com/hsz1273327/cognihub_pyeffectref/issues",
"Repository": "https://github.com/hsz1273327/cognihub_pyeffectref"
},
"split_keywords": [
"reactive",
" vue",
" effect",
" ref",
" cognihub",
" reactivedict",
" readonly",
" async",
" threading"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "de6295237bc3e211aaa2e2425a5324f4f3ab81063072e1e0cb23faa9e68b4af1",
"md5": "0d4611afb5550939d4129c321704e0b3",
"sha256": "8acd5c66d910e43d5b8d100346d76ea6c25dfeefc64ab41b5dbba5de4c743c3f"
},
"downloads": -1,
"filename": "cognihub_pyeffectref-0.2.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0d4611afb5550939d4129c321704e0b3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 21173,
"upload_time": "2025-07-23T07:51:58",
"upload_time_iso_8601": "2025-07-23T07:51:58.768879Z",
"url": "https://files.pythonhosted.org/packages/de/62/95237bc3e211aaa2e2425a5324f4f3ab81063072e1e0cb23faa9e68b4af1/cognihub_pyeffectref-0.2.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "aca8bc56eae86861b2c8bff93aed801b31ac99132c722be98a0d7664212747b4",
"md5": "b4bd3fc340c58b492ae400c2e9d4a337",
"sha256": "ac1ee6c90edbf5d2210baedf4a328a8b29170eb58a8b221055b9e25c110a6615"
},
"downloads": -1,
"filename": "cognihub_pyeffectref-0.2.0.tar.gz",
"has_sig": false,
"md5_digest": "b4bd3fc340c58b492ae400c2e9d4a337",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 45444,
"upload_time": "2025-07-23T07:52:00",
"upload_time_iso_8601": "2025-07-23T07:52:00.399403Z",
"url": "https://files.pythonhosted.org/packages/ac/a8/bc56eae86861b2c8bff93aed801b31ac99132c722be98a0d7664212747b4/cognihub_pyeffectref-0.2.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-23 07:52:00",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "hsz1273327",
"github_project": "cognihub_pyeffectref",
"travis_ci": false,
"coveralls": true,
"github_actions": false,
"lcname": "cognihub-pyeffectref"
}