Skip to content

Instantly share code, notes, and snippets.

@KuRRe8
Last active June 6, 2025 19:35
Show Gist options
  • Save KuRRe8/26bb1fd09b3e272c01fbe91a20dd3343 to your computer and use it in GitHub Desktop.
Save KuRRe8/26bb1fd09b3e272c01fbe91a20dd3343 to your computer and use it in GitHub Desktop.
small repo for personal use, welcome to contribute
import logging
import os
from datetime import datetime
# Define a global logger
def setup_logger(log_file="app.log", level=logging.INFO):
"""Setup and return a logger instance."""
logger = logging.getLogger()
logger.setLevel(level)
# Create file handler
file_handler = logging.FileHandler(log_file)
file_handler.setLevel(level)
# Create console handler
console_handler = logging.StreamHandler()
console_handler.setLevel(level)
# Define formatter and add it to handlers
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)
# Add handlers to the logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
logger.info('###Logger initialized.###')
return logger
# Initialize the global logger
current_date = datetime.now().strftime("%Y-%m-%d")
LOG_FILE_PATH = os.path.join(os.path.dirname(__file__), "logs", f"app.{datetime.now().strftime('%Y-%m-%d_%H-%M')}.log")
os.makedirs(os.path.dirname(LOG_FILE_PATH), exist_ok=True)
logger = setup_logger(log_file=LOG_FILE_PATH,level=config.settings.LOGGER_LVL)
# Example usage
if __name__ == "__main__":
logger.debug('Test the debug level log.')
logger.info('Test the info level log.')
logger.warning('Test the warning level log.')
logger.error('Test the error level log.')
logger.critical('Test the critical level log.')
#! shebang in first line not rendered as orange
#! important message in other lines
#- print("this code line is deleted.")
## unimportant comments
#PBS
#TODO todo is rendered
#FIXME
#BUG
#HACK
#NOTE
def onefunc(param1: float, param2: str, param3: list[str])-> dict|None:
"""_summary_
Args:
param1 (float): _description_
param2 (str): _description_
param3 (list[str]): _description_
Returns:
dict|None: _description_
"""
import sys
before = [str(m) for m in sys.modules]
import yourmoudule
after = [str(m) for m in sys.modules]
set([m.split('.')[0] for m in after if not m in before and not m.startswith('_')])
# use as a class method decorator, @classonlymethod def mymethod: pass
class classonlymethod(classmethod):
"""
decorator for class methods that should only be called on the class, not on instances.
"""
def __get__(self, instance, cls=None):
if instance is not None:
raise AttributeError("This method is available only on the class, not on instances.")
return super(classonlymethod, self).__get__(instance, cls)
def clear_screen():
import sys
sys.stdout.write("\033[2J\033[1;1H")
sys.stdout.flush()
#change the working dir to current file dir
import os
import sys
if "__file__" in globals():
os.chdir(os.path.dirname(os.path.abspath(__file__)))
"""
Python 中使用工厂模式
"""
"""
第一部分 构造工厂模板,构造产品接口抽象类,固化代码,可以被整个工程使用
"""
from abc import ABC, abstractmethod
from typing import Callable
# 抽象工厂模板
class Factory[T]:
def __init__(self) -> None:
self._creators: dict[str, Callable[..., T]] = {}
def register(self, name: str, creator: Callable[..., T]) -> None:
self._creators[name] = creator
def create(self, name: str, *args, **kwargs) -> T | None:
if name in self._creators:
return self._creators[name](*args, **kwargs)
return None
# 抽象基类:外卖订单
class Order(ABC):
def __init__(self, fee: float) -> None:
self.fee = fee
@abstractmethod
def dispatch(self) -> None:...
import threading
class SingletonMeta(type):
def __init__(cls, *args, **kwargs):
cls.__instance = None
cls.__lock = threading.RLock()
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
with cls.__lock:
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
"""
第二部分 生成具体产品的工厂的单例,有多少种场景就实例化多少工厂,最好每个文件最多一种具体工厂
"""
# 具体外卖工厂:绑定 Order 类型
class OrderFactory(Factory[Order], metaclass=SingletonMeta):...
# 或者类似于C++ 中的using语法简化实例化模板名称,但该名称只能用于注解,不能调用
type _OrderFactory = Factory[Order]
"""
第三部分 随着软件发布版本随时更新增加的具体产品,这里是外卖订单
"""
# 具体订单类
class MeiTuanOrder(Order):
def dispatch(self) -> None:
print(f"MeiTuan: 派送员已接单,正在配送中... 配送费: {self.fee} 元")
class JingDongOrder(Order):
def dispatch(self) -> None:
print(f"JingDong: 京东快递已发货,极速达... 配送费: {self.fee} 元")
class EleMeOrder(Order):
def dispatch(self) -> None:
print(f"EleMe: 饿了么骑手已接单,预计30分钟送达... 配送费: {self.fee} 元")
def setup():
orderfactory = OrderFactory() # 获取单例
orderfactory.register("meituan", MeiTuanOrder)
orderfactory.register("jingdong", JingDongOrder)
orderfactory.register("eleme", EleMeOrder)
setup()
"""
第四部分 最终使用者的代码
"""
# 使用示例
if __name__ == "__main__":
factory = OrderFactory() # 获取单例
order1 = factory.create("meituan", 5.0)
order2 = factory.create("jingdong", 8.5)
order3 = factory.create("eleme", 6.8)
if order1: order1.dispatch() # MeiTuan: 派送员已接单,正在配送中... 配送费: 5.0 元
if order2: order2.dispatch() # JingDong: 京东快递已发货,极速达... 配送费: 8.5 元
if order3: order3.dispatch() # EleMe: 饿了么骑手已接单,预计30分钟送达... 配送费: 6.8 元

Python编程之泛型

引言: 自 Python 3.5 起,类型注解成为官方语言特性,极大提升了代码的可读性与可维护性。它让调用者在使用函数或类时明确知道参数与返回值的预期类型,而且现代IDE智能提示也依赖于此。此外,在一些无法方便调试的场景如硬件控制或网络交互中,依赖类型注解和静态类型检查器在运行前发现问题变得尤为重要。

类型注解的发展过程中也暴露出一个需求:我们需要能表达“多种类型但性质统一”的情况,这正是泛型(Generic)存在的意义。

泛型函数

在3.12+ 版本中,可以使用如下写法,在方括号内定义了类型变量T和U,在函数签名中使用T和U来指定入参的类型。而输出值指定为包含分别为T类型U类型的两个元素的元组。

def pair[T, U](first: T, second: U) -> tuple[T, U]:
    return (first, second)

替代了旧式冗杂的写法

from typing import TypeVar, Generic

T = TypeVar('T')
U = TypeVar('U')

def pair(first: T, second: U) -> tuple[T, U]:
    return (first, second)

当然,我们还拥有类型约束写法确定类型上界

def pair[T: str, U: int](first: T, second: U) -> tuple[T, U]:
    return (first, second)

泛型类

我们可以使用如下写法定义一个泛型类ClassA,T是一个类型变量,约束为str类型。这样在类内各处都可以使用该泛型T作为类型注解,比如在方法method1中返回值类型也被指定为T。

class ClassA[T: str]:
    def method1(self) -> T:
        ...

用于替代以前的

from typing import Generic, TypeVar

_T_co = TypeVar("_T_co", covariant=True, bound=str)

class ClassA(Generic[_T_co]):
    def method1(self) -> _T_co:
        ...

可以注意到新式写法不再关注协变逆变概念。

类型别名,泛型别名

我们可以有别名typing.TypeAliasType

type Point2D = tuple[float,float] # type(Point2D)运行时输出<class 'typing.TypeAliasType'>
type Point3D = tuple[float,float,float] # 对类型的索引语法[]将会调用__class_getitem__方法(ENUM例外)。如果是自定义类型,则需要继承自内置类型,或者继承自typing.Generic然后手动实现__class_getitem__。

type ListOrSet[T] = list[T] | set[T] # 泛型别名,表示要么是包含若干T类型的列表,要么是包含若干T类型的集合。这里和直接构造list用法不同的是,无法指定存储多种元素类型的列表类型。
type StudentInfo[T] = tuple[str,bool,int,T] # 泛型别名,表示一个包含姓名、是否活跃、年龄和额外信息的元组,其中额外信息可以是任意类型T。

student001: StudentInfo[dict] = ("Bob", True, 22, {
    "Country": "US",
    "Hobby": "tennis",
    "Height": 182,
    "Married": False
})
# 这里的StudentInfo[dict]是将泛型实例化,代表最后一个具体类型是dict
    

相比起class定义一个类,以上写法可使得书写泛型更加简洁,但是丧失了强制性,并且在运行时无法使用isinstance检查(纯注解行为)。

仅针对StudentInfo这样场景的话,我们可以定义一个数据类,使用@dataclasses.dataclass装饰器

根据新式写法,我们可以构造一些python中没有内置的泛型。 值得注意的是,泛型的概念源于强类型语言,而python本身就是动态类型,运行时天然支持泛型,所以如此写法仅有助于静态类型检查

Python泛型和C++ STL容器的对应关系

Python 泛型类型 C++ STL 容器 说明
list[T] std::vector<T> 动态数组
collections.deque[T] std::deque<T> 双端队列,从 3.9 开始支持泛型
set[T] std::set<T> 集合
set[T] std::unordered_set<T> Python 的 set 本身就是基于哈希
dict[K, V] std::map<K, V> 键值映射
dict[K, V] std::unordered_map<K, V> Python 的 dict 是哈希表
tuple[T1, T2] std::pair<T1, T2> 固定长度的元组
tuple[T1, T2, ...] std::tuple<Ts...> 多值元组(异质)
T| None std::optional<T> 可选T类型
Callable[[A], R] std::function<R(A)> 函数类型(类型注解用)
tuple[T, ...] std::array<T, N> 数组

对于Python泛型的思考

Python中有两个方面和强类型语言比如C++有重大区别:

  • 类型系统
  • 面向对象

类型系统

众所周知,Python属于完全的动态类型语言。注解语法完全无法限制python的运行时类型行为,可以认为任何地方,包括全局和类函数的所有入参和返回值,还有全局变量和类属性,其类型都隐式地指定为typing.Any。这里的Any是指任意而不是任一。

C++的所有运行时类型,都是确定的某一类型。造成其根本性区别是在于C++编译期即把所有类型确定(即使有dynamic_cast<>亦或者类型擦除),根据不同的类型生成了不同的汇编指令的具体写法,而Python在运行期依旧保留了所有数据所有类的元信息,只有在具体执行到该代码处才根据元信息把类型确定下来(同时此举给python带来了反射reflection和自省introspection的能力)。

泛型写法对于C++和Python更像一场双向奔赴:C++的模板使得其拥有了“一种定义,多种版本”的能力,尽管只定义了一处模板,模板实例化却可以支持很多不同的参数类型、返回类型(但不是任意类型)。Python的泛型则在提示和静态类型检查层面,局限了原本类型的任意性。

比如:

def pair[T, U](first: T, second: U) -> tuple[T, U]:
    return (first, second)

这里在语义上告知,返回值是和输入值一致的类型,不再任意。另外,类型上界bound的语法,也在语义和类型检查上限制了类型的范围。这样使得Python的任意类型收缩为某些类型。Python当中的泛型约定形似C++中的concepts之于模板。

再说类型别名:一个类型别名的语句对于运行时依旧没有任何影响(如果不考虑专为类型检查器生成的dunder方法诸如“__bound__” “__make_typealias__”之类),并且类型别名不会生成新的类型对象,不能用于 isinstance 检查,也不能用于构造数据,它仅是注解层的符号替换,依旧依托原始类型的构造器,比如:

type Point2D = tuple[float,float]
pointA: Point2D = (2.1, 4.5)
print(type(pointA)) # <class 'tuple'>

在运行时并无Point2D这个类型。 那么引起这样一个思考,到底要不要使用这种仅有提示意义的语法呢?是不是应该所有类型都使用class定义以保证运行时的合法性呢?我觉得可以放在下面引申的面向对象说完以后一起给出结论。

面向对象

Python和C++都是支持面向对象的语言,都支持继承和多态。但Python有一个灵活的特性即鸭子类型duck typing。其意:只要会呱呱叫就认为是鸭子,只要有特定的行为就认为是某种类型,而不需要显式继承。鸭子类型一般和Protocol配合使用方便接受静态类型检查器检查。

C++没有这种隐式注册子类型的行为,所有继承都需要从祖宗辈开始显式继承。这在定义类的时候,语义会非常清晰: 比如继承了某些接口,那么子类必须实现/重写接口的纯虚函数。这样的好处是规范,能尽早发现实现过程的问题,如果没有实现虚函数,静态检查期和编译期报错。

python使用鸭子类型的话,直到运行期都不一定能够明显的报错。即使mypy或者pyright能够静态检查,但这种松散的完全解耦的关系在项目复杂时候很不直观,完全背离了面向对象中继承多态的严谨逻辑。

所以我会拒绝以下的写法:

from typing import Protocol

class Speaker(Protocol):
    def speak(self) -> None: ...

class Cat:
    def speak(self):
        print("Meow")

class Dog:
    def speak(self):
        print("Woof")
    
def make_it_speak(animal: Speaker):
    animal.speak()

make_it_speak(Cat())  # Meow
make_it_speak(Dog())  # Woof

转而使用更加规范的

from abc import ABCMeta, abstractmethod
from typing import override

class Speakable(metaclass=ABCMeta):
    @abstractmethod
    def speak(self) -> None:...

class Cat(Speakable):
    @override
    def speak(self) -> None:
        print("Meow")

class Dog(Speakable):
    @override
    def speak(self) -> None:
        print("Woof")

def make_it_speak(animal: Speakable):
    animal.speak()

make_it_speak(Cat())  # Meow
make_it_speak(Dog())  # Woof

可以看出来,我对于Python面向对象的继承多态行为还是比较pedantic的,现在可以回到上面类型系统的问题:是否应该使用type别名?

结论:只有在非常简单的场景下可以使用类型别名typing.TypeAliasType而没必要去定义一个运行时可见的class,任何其他情况都应该定义明确的class,传参或者返回也应该指定该class,构造数据时候使用该类型()调用。

比如之前的这个例子:

type Point2D = tuple[float,float]
type Point3D = tuple[float,float,float]
type StudentInfo[T] = tuple[str,bool,int,T]

student001: StudentInfo[dict] = ("Bob", True, 22, {
    "Country": "US",
    "Hobby": "tennis",
    "Height": 182,
    "Married": False
})

pointA: Point2D = (2.1, 4.5)

其中Point2D 和 Point3D 别名对应的类型相对简单,使用数据时候也有简单的构造方式pointA: Point2D = (2.1, 4.5),所以一般不用增加一个类。

StudentInfo相对复杂,使用别名方式无法进行类型具体内容的约束,还使用tuple的构造方式如果笔误也不一定有运行期的报错,我们改为以下形式

class StudentInfo[T](tuple):
    def __new__(cls, name: str, is_active: bool, age: int, extra: T):
        return super().__new__(cls, (name, is_active, age, extra))

    @property
    def name(self) -> str:
        return self[0]

    @property
    def is_active(self) -> bool:
        return self[1]

    @property
    def age(self) -> int:
        return self[2]

    @property
    def extra(self) -> T:
        return self[3]

student001 = StudentInfo[dict]("Bob", True, 22, {
    "Country": "US",
    "Hobby": "tennis",
    "Height": 182,
    "Married": False
})

print(student001.name)            # Bob
print(student001[2])              # 22
print(type(student001.extra))     # <class 'dict'>

结论

泛型在近年来逐步受到程序员社区的重视。我们在未来的编码实践中,可以尝试利用泛型的优势提升我们代码的质量。

# set random generator seed
def make_training_deterministic(seed: int = 0):
'''Set random seed for reproducibility'''
np.random.seed(seed)
random.seed(seed)
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False
os.environ['PYTHONHASHSEED'] = str(seed)

Python Snippets

Useful code for daily development.

Use RICH

Showcase

image

Code

import time
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.markdown import Markdown
from rich.syntax import Syntax
from rich.progress import track, Progress, SpinnerColumn, BarColumn, TimeElapsedColumn
from rich.live import Live
from rich.columns import Columns
from rich.traceback import install

# 全局替换 traceback 为 rich 风格
install()
console = Console()

def show_text_styles():
    console.rule("[bold magenta]✨ Rich Text Demo")
    console.print("[bold green]成功![/bold green] Rich 可以渲染 [italic]富文本[/italic]、emoji 😎 和 [underline]下划线[/underline]!")

def show_table():
    table = Table(title="模型评估指标", style="cyan")
    table.add_column("模型", justify="center", style="green")
    table.add_column("准确率", justify="right")
    table.add_column("损失", justify="right")

    table.add_row("模型A", "0.912", "0.083")
    table.add_row("模型B", "0.875", "0.105")
    table.add_row("模型C", "0.943", "0.065")

    console.rule("[bold yellow]📊 表格展示")
    console.print(table)

def show_progress():
    console.rule("[bold blue]📦 模拟进度条")
    for _ in track(range(20), description="训练中..."):
        time.sleep(0.05)

def show_rich_progress():
    console.rule("[bold blue]🚀 高级进度条")
    with Progress(
        SpinnerColumn(),
        "[progress.description]{task.description}",
        BarColumn(),
        TimeElapsedColumn(),
        console=console,
    ) as progress:
        task = progress.add_task("加载模型...", total=30)
        for _ in range(30):
            time.sleep(0.05)
            progress.update(task, advance=1)

def show_live_columns():
    console.rule("[bold green]📦 实时布局演示")
    items = [Panel(f"[bold]{i}[/bold]", expand=True) for i in range(1, 10)]
    console.print(Columns(items))

def show_markdown_and_code():
    console.rule("[bold cyan]📝 Markdown & Code")
    md = Markdown("# 示例文档\n- 使用 **rich** 渲染 markdown\n- 轻松集成 CLI UI")
    console.print(md)

    code = """def hello(name):\n    print(f"Hello, {name}!")"""
    syntax = Syntax(code, "python", theme="monokai", line_numbers=True)
    console.print(syntax)

def show_panel():
    console.rule("[bold red]📌 信息面板")
    console.print(Panel.fit("[bold yellow]🎉 一切准备就绪,开始任务!", border_style="bright_yellow"))

def simulate_error():
    console.rule("[bold red]💥 异常展示(自动美化)")
    def boom():
        return 1 / 0  # 故意报错
    try:
        boom()
    except Exception:
        console.print_exception()

def main():
    show_text_styles()
    show_table()
    show_panel()
    show_markdown_and_code()
    show_progress()
    show_rich_progress()
    show_live_columns()
    simulate_error()

if __name__ == "__main__":
    main()
import threading
class SingletonMeta(type):
"""自定义元类"""
def __init__(cls, *args, **kwargs):
cls.__instance = None
cls.__lock = threading.RLock()
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
with cls.__lock:
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
class President(metaclass=SingletonMeta):
"""总统(单例类)"""
pass
# 上面是使用metaclass的单例,下面是使用装饰器的做法
from functools import wraps
from threading import RLock
def singleton(cls):
"""线程安全的单例装饰器"""
instances = {}
locker = RLock()
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
with locker:
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
# time profiling
import time
class Timer:
def __init__(self, name="Timer"):
self.name = name
def __enter__(self):
self.start = time.perf_counter()
return self
def __exit__(self, *args):
elapsed = time.perf_counter() - self.start
print(f"[{self.name}] Elapsed: {elapsed:.6f} seconds")
# 用法
with Timer("Sum 1 million"):
sum(range(1000000))
import os
def print_tree(path, prefix=""):
files = sorted(os.listdir(path))
for i, name in enumerate(files):
full_path = os.path.join(path, name)
connector = "└── " if i == len(files) - 1 else "├── "
print(prefix + connector + name)
if os.path.isdir(full_path):
extension = " " if i == len(files) - 1 else "│ "
print_tree(full_path, prefix + extension)
print_tree("/your/path/here")
import os
import json
from IPython.display import display, JSON
def get_directory_structure_with_human_readable_sizes(path):
"""
递归地构建目录结构,包含人类可读的文件大小。
Args:
path (str): 要遍历的路径。
Returns:
dict: 目录结构的 JSON 格式表示,人类可读的文件大小作为 value。
"""
try:
tree = {}
for entry in os.listdir(path):
full_path = os.path.join(path, entry)
if os.path.isfile(full_path):
try:
size_bytes = os.path.getsize(full_path)
tree[entry] = human_readable_size(size_bytes) # 获取并格式化文件大小
except OSError as e:
tree[entry] = f"Error getting size: {e}" # 处理无法获取大小的情况
elif os.path.isdir(full_path):
tree[entry] = get_directory_structure_with_human_readable_sizes(full_path) # 递归调用
else:
tree[entry] = "unknown"
except OSError as e:
return f"Error: {e}" # 处理权限等错误
return tree
def human_readable_size(size_bytes):
"""
将字节数转换为人类可读的格式(KB、MB、GB 等)。
Args:
size_bytes (int): 文件大小(以字节为单位)。
Returns:
str: 人类可读的文件大小字符串。
"""
if size_bytes == 0:
return "0 Bytes"
size_name = ("Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 2)
return f"{s} {size_name[i]}"
# 确保math模块被导入
import math
# 获取根目录的结构,包含人类可读的文件大小
root_structure = get_directory_structure_with_human_readable_sizes("/")
# 使用 IPython.display.JSON 显示
display(JSON(root_structure, expanded=False)) # 默认折叠以提高可读性
# 可选:将 JSON 字符串打印出来,以便在不支持 display 的情况下查看
# print(json.dumps(root_structure, indent=2))
import os
from IPython.display import display
import ipywidgets as widgets
def list_dir(path, show_hidden=False):
try:
items = sorted(os.listdir(path))
except PermissionError:
items = []
if not show_hidden:
items = [item for item in items if not item.startswith('.')]
return items
def make_browser(path, show_hidden):
box = widgets.VBox()
label = widgets.HTML(f"<b>{path}</b>")
children = []
for name in list_dir(path, show_hidden):
full_path = os.path.join(path, name)
if os.path.isdir(full_path):
toggle = widgets.ToggleButton(value=False, description=name, layout=widgets.Layout(width='auto'))
subbox = widgets.VBox()
def on_toggle_change(change, path=full_path, subbox=subbox):
if change['new']:
subbox.children = [make_browser(path, show_hidden)]
else:
subbox.children = []
toggle.observe(on_toggle_change, names='value')
children.append(widgets.VBox([toggle, subbox]))
else:
children.append(widgets.Label("📄 " + name))
box.children = [label] + children
return box
# 控制开关 + 文件浏览器绑定逻辑
def interactive_browser(start_path="/your/path/here"):
show_hidden_toggle = widgets.Checkbox(value=False, description="显示隐藏文件(.开头)")
output = widgets.Output()
def update_browser(change=None):
output.clear_output()
with output:
display(make_browser(start_path, show_hidden_toggle.value))
show_hidden_toggle.observe(update_browser, names='value')
update_browser() # 初始加载
display(widgets.VBox([show_hidden_toggle, output]))
# 调用函数启动浏览器
interactive_browser("/your/path/here")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment