Rust 学习笔记 22:共享状态并发 (Shared-State Concurrency)

Rust 学习笔记 22:共享状态并发 (Shared-State Concurrency) “Do not communicate by sharing memory; instead, share memory by communicating.” – Go Language Slogan 虽然 Go 倡导通过通信来共享内存(Channel),但 Rust 同样提供了强大的共享状态并发工具。而且得益于所有权系统,Rust 中的锁是线程安全的(Thread Safety),你很难写出有数据竞争的代码。 1. Mutex (互斥锁) Mutex (Mutual Exclusion) 让同一时刻只有一个线程访问数据。 Rust 的 Mutex 有两个特点: 必须要先获取锁,才能通过解引用访问内部数据。这保证了你如果不加锁,根本拿不到数据(编译器按头让你加锁)。 RAII:锁的释放在 Guard 离开作用域时自动发生,不需要手动 unlock。 1let m = Mutex::new(5); 2{ 3 let mut num = m.lock().unwrap(); // 获取锁,num 是 MutexGuard 4 *num = 6; // 修改数据 5} // 离开作用域,锁自动释放 2. 原子引用计数 Arc 要在多个线程间共享 Mutex,直接传是不行的(所有权规则)。 你可能会想到 Rc<T>,但 Rc 不是线程安全的。 我们需要 Arc<T> (Atomic Reference Counting)。 ...

2025-11-30 · 2 min · 223 words · 老墨

Rust 学习笔记 21:并发编程 (无畏并发)

Rust 学习笔记 21:并发编程 (无畏并发) “Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.” – Rob Pike 在很多语言中,并发编程是噩梦。数据竞争 (Data Race)、死锁 (Deadlock)、竞态条件 (Race Condition) 让人防不胜防。 Rust 号称 “Fearless Concurrency”(无畏并发)。它利用所有权和类型系统,在编译期就把数据竞争扼杀在摇篮里。 1. 使用线程 Rust 默认使用 1:1 线程模型,即一个 Rust 线程对应一个操作系统线程。 1use std::thread; 2use std::time::Duration; 3 4fn main() { 5 thread::spawn(|| { 6 for i in 1..10 { 7 println!("hi number {} from the spawned thread!", i); 8 thread::sleep(Duration::from_millis(1)); 9 } 10 }); 11 12 for i in 1..5 { 13 println!("hi number {} from the main thread!", i); 14 thread::sleep(Duration::from_millis(1)); 15 } 16} 注意:当主线程结束时,所有派生线程都会被强制终止,无论它们是否执行完。 ...

2025-11-29 · 2 min · 221 words · 老墨

Go高级教程:深入理解 GMP 调度器

大家好,我是极客老墨。 为什么 Go 语言能轻松支撑百万并发?为什么 Goroutine 切换成本这么低?这一切的背后,都站着一位神秘的大管家——GMP 调度器。这篇就用通俗易懂的语言,配合生动的比喻,带你深入理解 Go 高并发的核心秘密。 1. 为什么需要 GMP? 在很久很久以前(其实也就几十年前),我们写代码都是直接跟 线程 (Thread) 打交道。线程是操作系统(OS)调度的最小单位。 但是,线程这玩意儿太"贵"了: 内存占用高:一个线程栈大概要几 MB。 切换成本大:线程切换需要陷入内核态,保存寄存器、上下文,这简直就是"劳民伤财"。 这时候,Go 语言的设计师们拍案而起:“我们要造一种更轻量的线程!” 于是,Goroutine (协程) 诞生了。它初始只要几 KB,切换成本极低。 这就带来了一个问题:操作系统只认识线程,不认识 Goroutine。谁来负责把成千上万个 Goroutine 分配给 CPU 跑呢? 这就需要一个"中间商" —— Go 运行时调度器 (Scheduler)。 图示: Thread 与 Goroutine 的区别 2. GMP 模型大揭秘 GMP 其实是三个角色的缩写: G (Goroutine):我们写的代码任务,也就是协程。 M (Machine):工作线程(Thread),对应操作系统的真实线程。它是真正的干活人(搬砖工)。 P (Processor):逻辑处理器(Context),可以理解为"调度上下文"或"资源"。它是包工头,负责管理 G,并把 G 交给 M 去执行。 形象的比喻 想象一个大型搬砖工地: G (砖头):待搬运的任务。 M (工人):负责搬砖的劳动力。 P (手推车):工人必须推着车才能搬砖(因为车里装着搬砖工具和任务清单)。 如果没有 P(手推车),M(工人)就不知道该干啥。 ...

2025-11-28 · 2 min · 364 words · 老墨

[GoLang避坑实战-18] 实战 CLI 工具:30 分钟用 urfave/cli 构建你的极客武器

大家好,我是极客老墨。 上一篇我们写了个小工具——一个简单的命令行计算器,需要从命令行读参数, 我们直接通过 os.Args 来解析命令行参数,这种方式太底层、太原始了,非常容易出错。 说实话,Go 虽然内置了 flag 包,但用起来仍然不是很方便。想要做个像样的 CLI 工具,光靠 flag 远远不够。 今天我们就来聊聊如何用 Go 社区最受欢迎的 urfave/cli 框架,打造真正的极客工具。 为什么不用 flag 包? Go 标准库的 flag 包确实能解析参数,但问题是: 1// 用 flag 包写个简单的工具 2var name = flag.String("name", "", "your name") 3var age = flag.Int("age", 0, "your age") 4 5flag.Parse() 6 7if *name == "" { 8 fmt.Println("name is required") 9 os.Exit(1) 10} 看到没?每个参数都要手动验证,帮助信息要自己拼,子命令?不存在的,得自己实现。 写个小工具还行,但要做个像 git、docker 那样的专业 CLI,光靠 flag 真的会疯。 ...

2025-11-20 · 7 min · 1448 words · 老墨

Python教程39:元类(Metaclass)

Python教程39:元类(Metaclass) “元类就是深度魔法,99%的用户应该根本不必为此操心。” —— Tim Peters 元类是Python的高级特性,用于创建类的"类"。今天我们揭开元类的神秘面纱,理解Python对象系统的底层机制。 1. 什么是元类 一切皆对象 在Python中,类也是对象: 1class MyClass: 2 pass 3 4# 类是对象 5print(type(MyClass)) # <class 'type'> 6print(isinstance(MyClass, type)) # True 7 8# 类可以赋值、作为参数 9AnotherName = MyClass 10def func(cls): 11 return cls() 元类(Metaclass): 创建类的"类" type是Python的默认元类 类是元类的实例 1# 类是type的实例 2class MyClass: 3 pass 4 5print(type(MyClass)) # <class 'type'> 6 7# 等价于 8MyClass = type('MyClass', (), {}) type的三种用法 1# 用法1:查看对象类型 2print(type(5)) # <class 'int'> 3 4# 用法2:动态创建类 5# type(name, bases, dict) 6MyClass = type('MyClass', (), {'x': 5}) 7obj = MyClass() 8print(obj.x) # 5 9 10# 用法3:作为元类(继承type) 11class MyMeta(type): 12 pass 2. 动态创建类 使用type动态创建类: ...

2025-11-13 · 4 min · 670 words · 老墨

[GoLang避坑实战-17] 从零撸一个计算器:基础知识的大合练

大家好,我是极客老墨。 学了这么多语法,是时候写个完整项目了。光看代码片段,很难理解 Go 项目是怎么组织的。这篇我们从零开始,写一个命令行工具,看看真实项目的结构和开发流程。 项目目标 我们要做一个简单的命令行计算器,支持基本的数学运算。 功能需求 支持加减乘除运算 命令行参数输入 彩色输出结果 错误处理 技术要点 Go Modules 依赖管理 标准项目结构 包的导入和使用 第三方库集成 交叉编译 初始化项目 第一步是创建项目目录并初始化模块。 创建项目 1cd go-tutorial-code 2# 创建项目目录 3mkdir 15-project-example 4cd 15-project-example 5 6# 初始化 Go Module 7go mod init github.com/gkmz/calc 这里老墨为了教程的需要,把代码放到了 go-tutorial-code, 并且模块名称没有与文件夹名称一致。 生成的 go.mod: 1module github.com/gkmz/calc 2 3go 1.24 要点: 模块名通常是代码仓库地址 go.mod 是项目的起点 初始化后就可以开始写代码了 项目结构 Go 项目有约定俗成的目录结构。 标准布局 115-project-example/ 2├── go.mod # 模块定义 3├── go.sum # 依赖校验 4├── cmd/ # 命令行工具目录 5│ └── calc/ 6│ └── main.go 7├── pkg/ # 可导出的库代码 8│ └── calculator/ 9│ ├── calculator.go 10│ └── calculator_test.go 11├── internal/ # 私有代码(不可被外部导入) 12│ └── utils/ 13│ └── helper.go 14├── README.md # 项目说明 15└── Makefile # 构建脚本(可选) 目录说明: ...

2025-11-10 · 8 min · 1514 words · 老墨

Rust 学习笔记 20:项目实战二:构建 grep 命令行工具 (minigrep)

Rust 学习笔记 20:项目实战二:minigrep “UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity.” – Dennis Ritchie 到目前为止,我们已经学习了 Rust 的大部分核心特性。现在,让我们把它们串起来,复刻一个经典的命令行工具:grep。 我们的目标是创建一个 minigrep,它接受一个查询字符串和一个文件名,然后打印出文件中包含查询字符串的行。 1. 需求分析 用法: 1$ cargo run -- searchstring example-filename.txt 功能点: 读取命令行参数。 读取文件内容。 筛选包含关键词的行。 错误处理(用户没传参数?文件不存在?)。 关注点分离:main.rs 负责处理参数和系统调用,lib.rs 负责核心逻辑。 TDD:测试驱动开发。 环境变量:支持 IGNORE_CASE=1 进行大小写不敏感搜索。 2. 核心代码演进 我们将代码分为 main.rs 和 lib.rs。 2.1 参数解析与配置 在 src/lib.rs 中定义 Config 结构体: 1use std::env; 2 3pub struct Config { 4 pub query: String, 5 pub file_path: String, 6 pub ignore_case: bool, 7} 8 9impl Config { 10 pub fn build(mut args: impl Iterator<Item = String>) -> Result<Config, &'static str> { 11 args.next(); // 也就是程序名,通常忽略 12 13 let query = match args.next() { 14 Some(arg) => arg, 15 None => return Err("Didn't get a query string"), 16 }; 17 18 let file_path = match args.next() { 19 Some(arg) => arg, 20 None => return Err("Didn't get a file path"), 21 }; 22 23 let ignore_case = env::var("IGNORE_CASE").is_ok(); 24 25 Ok(Config { 26 query, 27 file_path, 28 ignore_case, 29 }) 30 } 31} 注意这里使用了 impl Iterator,这样我们可以直接消费 env::args(),更加高效。 ...

2025-11-04 · 2 min · 344 words · 老墨

[GoLang避坑实战-16] "干净利落!手把手带你搭一套企业级 Go 项目目录

大家好,我是极客老墨。 好的项目结构能让代码更易理解、易维护、易扩展。Go 社区有一套被广泛认可的标准布局,这篇就聊聊如何组织 Go 项目,让代码结构清晰合理。 1. 标准项目布局 参考:golang-standards/project-layout 1myproject/ 2├── cmd/ # 主程序入口 3│ ├── server/ 4│ │ └── main.go # 服务端入口 5│ └── cli/ 6│ └── main.go # 命令行工具入口 7├── internal/ # 私有代码(不可被外部导入) 8│ ├── handler/ 9│ ├── service/ 10│ └── repository/ 11├── pkg/ # 公共库(可被外部导入) 12│ ├── util/ 13│ └── validator/ 14├── api/ # API 定义(OpenAPI/Swagger) 15│ └── openapi.yaml 16├── web/ # Web 静态资源 17│ ├── static/ 18│ └── templates/ 19├── configs/ # 配置文件 20│ ├── config.yaml 21│ └── config.prod.yaml 22├── scripts/ # 脚本(构建、部署等) 23│ ├── build.sh 24│ └── deploy.sh 25├── test/ # 额外的测试数据 26│ └── fixtures/ 27├── docs/ # 文档 28│ └── API.md 29├── go.mod 30├── go.sum 31├── Makefile # 构建脚本 32├── Dockerfile 33└── README.md 2. 核心目录详解 2.1 cmd/ 存放主程序入口,每个子目录对应一个可执行文件。 ...

2025-10-29 · 8 min · 1597 words · 老墨

Python教程38:多重继承与MRO

Python教程38:多重继承与MRO “大道至简,大巧若拙。” Python支持多重继承,一个类可以继承多个父类。今天我们学习多重继承的机制、方法解析顺序(MRO)以及如何避免常见陷阱。 1. 什么是多重继承 单继承 vs 多重继承 1# 单继承:一个父类 2class Parent: 3 pass 4 5class Child(Parent): 6 pass 7 8# 多重继承:多个父类 9class Father: 10 def skill(self): 11 return "父亲的技能" 12 13class Mother: 14 def talent(self): 15 return "母亲的天赋" 16 17class Child(Father, Mother): 18 """继承两个父类""" 19 pass 20 21# 子类拥有所有父类的方法 22c = Child() 23print(c.skill()) # 父亲的技能 24print(c.talent()) # 母亲的天赋 2. 方法解析顺序(MRO) 当多个父类有同名方法时,Python如何决定调用哪个? 基本规则 1class A: 2 def method(self): 3 return "A的方法" 4 5class B: 6 def method(self): 7 return "B的方法" 8 9class C(A, B): 10 """继承顺序:A, B""" 11 pass 12 13c = C() 14print(c.method()) # A的方法(优先使用第一个父类) 15 16# 查看MRO 17print(C.__mro__) 18# (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>) MRO(Method Resolution Order): ...

2025-10-24 · 4 min · 646 words · 老墨

Python教程37:抽象基类(ABC)

Python教程37:抽象基类(ABC) “约定优于配置。” 抽象基类(Abstract Base Class, ABC)是Python中定义接口规范的机制。今天我们学习如何使用abc模块创建抽象类,强制子类实现特定方法。 1. 什么是抽象基类 问题场景 没有抽象类时,接口规范靠文档和约定: 1class Shape: 2 """ 3 形状基类 4 - 子类应该实现area()和perimeter() 5 - 但没有强制机制 6 """ 7 def area(self): 8 raise NotImplementedError("子类必须实现area方法") 9 10 def perimeter(self): 11 raise NotImplementedError("子类必须实现perimeter方法") 12 13class Circle(Shape): 14 def __init__(self, radius): 15 self.radius = radius 16 17 def area(self): 18 import math 19 return math.pi * self.radius ** 2 20 21 # 忘记实现perimeter()了! 22 23# 问题:可以实例化不完整的类 24c = Circle(5) 25print(c.area()) # OK 26# print(c.perimeter()) # 运行时才报错! 抽象基类解决方案 1from abc import ABC, abstractmethod 2 3class Shape(ABC): 4 """ 5 抽象形状类 6 - 继承ABC 7 - 使用@abstractmethod标记抽象方法 8 - 不能直接实例化 9 - 强制子类实现所有抽象方法 10 """ 11 12 @abstractmethod 13 def area(self): 14 """计算面积(抽象方法)""" 15 pass 16 17 @abstractmethod 18 def perimeter(self): 19 """计算周长(抽象方法)""" 20 pass 21 22class Circle(Shape): 23 def __init__(self, radius): 24 self.radius = radius 25 26 def area(self): 27 import math 28 return math.pi * self.radius ** 2 29 30 # 如果不实现perimeter(),无法实例化 31 32# circle = Shape() # TypeError: 无法实例化抽象类 33 34class CompleteCircle(Shape): 35 def __init__(self, radius): 36 self.radius = radius 37 38 def area(self): 39 import math 40 return math.pi * self.radius ** 2 41 42 def perimeter(self): 43 import math 44 return 2 * math.pi * self.radius 45 46# 完整实现所有抽象方法才能实例化 47c = CompleteCircle(5) 48print(c.area()) # 78.54 49print(c.perimeter()) # 31.42 ABC的优势: ...

2025-10-22 · 5 min · 1005 words · 老墨

Python教程36:属性(Property)与描述符

Python教程36:属性(Property)与描述符 “细节决定成败。” Python提供了强大的属性管理机制,让我们能够优雅地控制属性的访问。今天我们学习@property装饰器和描述符协议,掌握Python属性的高级用法。 1. 为什么需要Property 问题场景 直接访问属性缺乏控制: 1class Person: 2 def __init__(self, age): 3 self.age = age # 公开属性 4 5p = Person(25) 6print(p.age) # 25 7 8# 问题:可以设置非法值 9p.age = -10 # 负数年龄? 10p.age = "abc" # 字符串年龄? 传统解决方法:getter/setter 1class Person: 2 def __init__(self, age): 3 self._age = age # 私有属性 4 5 def get_age(self): 6 """获取年龄""" 7 return self._age 8 9 def set_age(self, value): 10 """设置年龄(带验证)""" 11 if not isinstance(value, int): 12 raise TypeError("年龄必须是整数") 13 if value < 0 or value > 150: 14 raise ValueError("年龄不合理") 15 self._age = value 16 17p = Person(25) 18print(p.get_age()) # 25 19p.set_age(30) # OK 20# p.set_age(-10) # ValueError 问题: 使用不方便:p.get_age()而不是p.age 破坏现有代码:如果后来添加验证,需要修改所有调用 Python解决方案:@property 1class Person: 2 def __init__(self, age): 3 self._age = age 4 5 @property 6 def age(self): 7 """ 8 getter方法 9 - 像访问属性一样调用:p.age 10 - 但实际调用的是方法 11 """ 12 return self._age 13 14 @age.setter 15 def age(self, value): 16 """ 17 setter方法 18 - 像赋值一样调用:p.age = 30 19 - 可以添加验证逻辑 20 """ 21 if not isinstance(value, int): 22 raise TypeError("年龄必须是整数") 23 if value < 0 or value > 150: 24 raise ValueError("年龄不合理") 25 self._age = value 26 27# 使用 28p = Person(25) 29print(p.age) # 调用getter:25 30p.age = 30 # 调用setter:OK 31# p.age = -10 # ValueError 优势: ...

2025-09-14 · 8 min · 1524 words · 老墨

环境变量与配置管理

大家好,我是极客老墨。 同一份代码需要在开发、测试、生产等不同环境运行。配置管理让我们能够灵活切换环境,而不需要修改代码。这篇就聊聊 Go 的配置管理,从最简单的环境变量到强大的 Viper 库。 1. 环境变量基础 1.1 读取环境变量 1import ( 2 "fmt" 3 "os" 4) 5 6func main() { 7 // 读取环境变量 8 dbHost := os.Getenv("DB_HOST") 9 if dbHost == "" { 10 dbHost = "localhost" // 默认值 11 } 12 13 fmt.Println("DB Host:", dbHost) 14 15 // 检查环境变量是否存在 16 port, exists := os.LookupEnv("PORT") 17 if !exists { 18 port = "8080" 19 } 20} 1.2 设置环境变量 1// 在程序中设置(仅影响当前进程) 2os.Setenv("API_KEY", "secret123") 3 4// 在 shell 中设置 5// export DB_HOST=localhost 6// export DB_PORT=3306 2. godotenv:.env 文件 2.1 安装 1go get -u github.com/joho/godotenv 2.2 使用 创建 .env 文件: ...

2025-09-11 · 4 min · 670 words · 老墨

Rust 学习笔记 19:智能指针 (Smart Pointers)

Rust 学习笔记 19:智能指针 (Smart Pointers) “Pointer, I choose you!” 在 C++ 中,为了管理内存,我们有 unique_ptr 和 shared_ptr。 Rust 也有类似这一套,而且它是内存安全模型的重要补充。 普通引用 &T 和 &mut T 只是单纯的借用,不拥有数据。 而智能指针通常拥有它们指向的数据。 1. Box 最简单的智能指针。它把数据分配在堆上,栈上只留一个指针。 1let b = Box::new(5); 2println!("b = {}", b); 用途: 当类型大小在编译期无法确定时(比如递归类型 Cons List)。 当你拥有大量数据,想转移所有权但不想拷贝数据时。 Trait Objects (Box<dyn Trait>)。 2. Deref 和 Drop Trait 智能指针之所以"智能",是因为它们实现了: Deref Trait:允许像普通引用一样使用解引用运算符 *。这就是为什么 *b 能取到 5。 Drop Trait:允许自定义在清理时运行的代码(析构函数)。 3. Rc (引用计数) Rc (Reference Counting) 允许数据有多个所有者。 比如图结构,一个节点可能被多条边指向。 1let a = Rc::new(5); 2let b = Rc::clone(&a); // 引用计数 +1 3let c = Rc::clone(&a); // 引用计数 +1 Rc::clone 不会深拷贝数据,只会增加引用计数。当所有 Rc 都离开作用域,计数归零,数据才被释放。 注意:Rc 只能用于单线程。多线程要用 Arc (Atomic Rc)。 ...

2025-08-28 · 1 min · 151 words · 老墨

Python教程35:类方法与静态方法

Python教程35:类方法与静态方法 “工欲善其事,必先利其器。” Python提供了三种方法类型:实例方法、类方法、静态方法。今天我们学习类方法(@classmethod)和静态方法(@staticmethod),理解它们的区别和使用场景。 1. 三种方法类型对比 实例方法(Instance Method) 我们最常用的方法: 1class MyClass: 2 def instance_method(self): 3 """ 4 实例方法 5 - 第一个参数是self(实例本身) 6 - 可以访问实例属性和类属性 7 - 通过实例调用 8 """ 9 print(f"Called instance_method of {self}") 10 return "instance method" 11 12obj = MyClass() 13obj.instance_method() # 正常调用 类方法(Class Method) 使用@classmethod装饰器: 1class MyClass: 2 @classmethod 3 def class_method(cls): 4 """ 5 类方法 6 - 第一个参数是cls(类本身) 7 - 可以访问类属性,不能访问实例属性 8 - 通过类或实例调用 9 """ 10 print(f"Called class_method of {cls}") 11 return "class method" 12 13# 通过类调用 14MyClass.class_method() 15 16# 通过实例调用也可以 17obj = MyClass() 18obj.class_method() 静态方法(Static Method) 使用@staticmethod装饰器: ...

2025-08-26 · 6 min · 1150 words · 老墨

Rust 学习笔记 18:闭包与迭代器 (Closures & Iterators)

Rust 学习笔记 18:闭包与迭代器 (Closures & Iterators) “Functional programming is like backwards coding, everything is immutable and stateless… wait, Rust allows mutability?” Rust 不仅仅是系统编程语言,它还拥有强大的函数式编程特性。其中最核心的两个就是:闭包和迭代器。 1. 闭包 (Closures) 闭包就是可以捕获其环境的匿名函数。 在 Go 中,我们经常写 func() { variableInOuterScope = 1 }。Rust 的闭包语法有点像 Ruby 的管道符 | |。 1let x = 4; 2let equal_to_x = |z| z == x; // 捕获了 x 3let y = 4; 4assert!(equal_to_x(y)); 类型推断: 闭包不需要像函数那样强制标注参数和返回值类型,编译器会自动推断。 捕获方式: 闭包可以通过三种方式捕获环境,对应三个 Trait: FnOnce:获取捕获变量的所有权(Move)。闭包只能被调用一次。 FnMut:获取可变借用。可以修改环境。 Fn:获取不可变借用。 2. 迭代器 (Iterators) 迭代器模式允许你对一个序列进行遍历。 在 Rust 中,迭代器是惰性 (Lazy) 的。如果你不调用 collect() 或 sum() 等消耗方法,迭代器本身什么都不做。 ...

2025-08-20 · 1 min · 184 words · 老墨

模糊测试入门 (Fuzzing)

大家好,我是极客老墨。 单元测试只能测试你想到的情况。空字符串、负数、超长输入,这些边界情况很容易遗漏。模糊测试能自动生成大量随机输入,帮你发现那些没想到的 Bug。 这篇就聊聊 Go 的模糊测试,看看它是怎么帮我们提升代码质量的。 什么是模糊测试 模糊测试(Fuzzing)是一种自动化测试技术,通过生成随机输入来发现程序的异常。 核心思想 1// 传统单元测试:测试已知输入 2func TestAdd(t *testing.T) { 3 if Add(2, 3) != 5 { 4 t.Error("2 + 3 should be 5") 5 } 6 if Add(0, 0) != 0 { 7 t.Error("0 + 0 should be 0") 8 } 9} 10 11// 模糊测试:测试大量随机输入 12func FuzzAdd(f *testing.F) { 13 f.Fuzz(func(t *testing.T, a, b int) { 14 result := Add(a, b) 15 // 检查属性而不是具体值 16 if a > 0 && b > 0 && result <= 0 { 17 t.Errorf("Add(%d, %d) = %d, overflow?", a, b, result) 18 } 19 }) 20} 区别: ...

2025-08-20 · 8 min · 1548 words · 老墨

Python教程34:魔术方法(Magic Methods)

Python教程34:魔术方法(Magic Methods) “命名即魔法。” Python的魔术方法(也叫特殊方法、双下划线方法)是Python面向对象的核心特性。它们让你的类能够像内置类型一样工作,实现运算符重载、容器协议等高级功能。 1. 什么是魔术方法 魔术方法(Magic Methods): 双下划线开头和结尾的方法:__method__ Python自动调用,不需要显式调用 让类支持Python的特殊语法 也叫"dunder methods"(double underscore) 为什么需要魔术方法: 运算符重载:让对象支持+、-、*等运算 容器协议:让对象可迭代、可索引 上下文管理:支持with语句 对象表示:自定义打印输出 属性访问:控制属性读写 简单示例 1class Point: 2 """二维点""" 3 def __init__(self, x, y): 4 """初始化(魔术方法)""" 5 self.x = x 6 self.y = y 7 8 def __str__(self): 9 """字符串表示(魔术方法)""" 10 return f"Point({self.x}, {self.y})" 11 12 def __add__(self, other): 13 """加法运算符(魔术方法)""" 14 return Point(self.x + other.x, self.y + other.y) 15 16# 使用 17p1 = Point(1, 2) 18p2 = Point(3, 4) 19 20print(p1) # 调用__str__: Point(1, 2) 21p3 = p1 + p2 # 调用__add__ 22print(p3) # Point(4, 6) 2. 对象创建和销毁 new:创建实例 1class Singleton: 2 """ 3 单例模式 4 - __new__在__init__之前调用 5 - __new__负责创建实例 6 - __new__是类方法 7 """ 8 _instance = None 9 10 def __new__(cls): 11 """ 12 创建实例 13 - cls是类本身 14 - 必须返回实例 15 """ 16 if cls._instance is None: 17 print("Creating singleton instance") 18 cls._instance = super().__new__(cls) 19 return cls._instance 20 21 def __init__(self): 22 """初始化实例""" 23 print("Initializing instance") 24 25# 使用 26s1 = Singleton() # Creating singleton instance, Initializing instance 27s2 = Singleton() # Initializing instance (不创建新实例) 28print(s1 is s2) # True(同一个实例) init:初始化 我们已经很熟悉了: ...

2025-07-28 · 8 min · 1524 words · 老墨

日志管理:从 log 到 zap

大家好,我是极客老墨。 排查问题时,日志是第一手段。服务挂了、请求慢了、数据错了,都要靠日志定位。但日志写不好,要么找不到关键信息,要么性能拖累整个系统。 这篇就聊聊 Go 的日志管理,从标准库到高性能方案,看看怎么写好日志。 标准库 log Go 自带的 log 包,简单但功能有限。 基本使用 1package main 2 3import ( 4 "log" 5) 6 7func main() { 8 // 基本输出 9 log.Println("Server started") 10 11 // 格式化输出 12 log.Printf("User %s logged in", "admin") 13 14 // 带前缀 15 log.SetPrefix("[MyApp] ") 16 log.Println("With prefix") 17 18 // 输出:[MyApp] 2025/07/18 10:00:00 With prefix 19} 要点: log.Println 输出日志 log.Printf 格式化输出 log.SetPrefix 设置前缀 默认输出到 stderr 自定义 Logger 1import ( 2 "log" 3 "os" 4) 5 6func main() { 7 // 创建自定义 Logger 8 logger := log.New( 9 os.Stdout, // 输出目标 10 "[INFO] ", // 前缀 11 log.Ldate|log.Ltime|log.Lshortfile, // 标志 12 ) 13 14 logger.Println("Custom logger message") 15 // 输出:[INFO] 2025/07/18 10:00:00 main.go:15: Custom logger message 16} 标志选项: ...

2025-07-18 · 7 min · 1425 words · 老墨

Python教程33:多态

Python教程33:多态 “一个接口,多种实现。” 多态是面向对象编程三大特性(封装、继承、多态)的最后一个。今天我们学习Python的多态特性,理解如何让代码更灵活、更易扩展。 1. 什么是多态 多态(Polymorphism): Poly(多个)+ morph(形态) 同一个接口,不同的实现 不同对象对同一消息的不同响应 为什么需要多态: 灵活性:通过父类引用调用子类方法 可扩展:添加新类无需修改现有代码 统一接口:不同对象用相同方式操作 解耦合:调用者不关心对象类型 简单示例 1class Animal: 2 """动物基类""" 3 def speak(self): 4 pass 5 6class Dog(Animal): 7 def speak(self): 8 return "Woof!" 9 10class Cat(Animal): 11 def speak(self): 12 return "Meow!" 13 14class Cow(Animal): 15 def speak(self): 16 return "Moo!" 17 18# 多态:相同的方法调用,不同的行为 19def animal_sound(animal): 20 """ 21 统一的接口 22 - 参数是Animal类型 23 - 不关心具体是什么动物 24 - 调用speak()得到不同的结果 25 """ 26 print(animal.speak()) 27 28# 使用 29animals = [Dog(), Cat(), Cow()] 30for animal in animals: 31 animal_sound(animal) 32# 输出: 33# Woof! 34# Meow! 35# Moo! 多态的好处: ...

2025-06-24 · 5 min · 987 words · 老墨

[GoLang避坑实战-11] 万物皆 Reader:像老鸟一样玩转 Go 语言的 IO 流

大家好,我是极客老墨。 在很多语言里,文件操作和网络操作是两套完全不同的 API。但在 Go 语言里,无论你是读文件、读网络请求,还是读一段内存字符串,你面对的通常都是同一个东西:io.Reader。 这种**“万物皆 Reader”**的设计哲学,是 Go 语言简洁高效的灵魂所在。这篇我们就来深度拆解 Go 的 IO 体系,让你不仅会用,还能写出高性能的 IO 代码。 1. 核心基石:Reader 与 Writer 接口 Go 的 IO 体系建立在两个极简的接口之上: 1type Reader interface { 2 Read(p []byte) (n int, err error) 3} 4 5type Writer interface { 6 Write(p []byte) (n int, err error) 7} 为什么这两个接口牛逼? 因为它屏蔽了底层实现。只要一个类型实现了 Read 方法,它就是 Reader。你可以把一个“读取文件”的 Reader 直接传给一个“处理 HTTP 响应”的函数。 老墨避坑指南: Read 方法里有个细节——io.EOF。它表示“文件结束”,虽然它是一个 error 类型,但在逻辑处理中,它通常标志着读取成功的终点,而不是程序出错了。所以处理时要先看 n > 0 处理数据,再看 err == io.EOF 退出循环。 ...

2025-06-19 · 2 min · 371 words · 老墨

Rust 学习笔记 17:自动化测试 (Automated Testing)

Rust 学习笔记 17:自动化测试 (Automated Testing) “Program testing can be a very effective way to show the presence of bugs, but it is hopelessly inadequate for showing their absence.” – Edsger W. Dijkstra 在 Go 中,我们创建 _test.go 文件,写 func TestXxx(t *testing.T)。 在 Rust 中,测试是一等公民。你不需要特殊的目录结构(单元测试),也不需要额外的测试框架。 1. 单元测试 (Unit Tests) 按照惯例,Rust 的单元测试直接写在源代码文件中,通常放在底部的 tests 模块里。 1// src/lib.rs 2 3fn add(a: i32, b: i32) -> i32 { 4 a + b 5} 6 7#[cfg(test)] 8mod tests { 9 use super::*; // 引入父模块的所有内容 10 11 #[test] 12 fn it_works() { 13 assert_eq!(add(2, 2), 4); 14 } 15} #[cfg(test)]:告诉编译器,只有在运行 cargo test 时才编译这个模块。生成的二进制文件不会包含这些测试代码,零空间开销。 #[test]:标记一个函数为测试函数。 assert_eq! / assert!:断言宏。 2. 测试私有函数 Rust 的单元测试允许测试私有函数!这是很多语言做不到的(Java 需要反射或放宽可见性)。 因为测试模块就定义在源码文件内部,且是子模块,它天然拥有访问父模块私有内容的权限。 ...

2025-06-10 · 2 min · 226 words · 老墨

[GoLang避坑实战-15] Go Module 救命指南:彻底告别依赖冲突和环境混乱

大家好,我是极客老墨。 Go 1.11 之前,依赖管理是个大麻烦。GOPATH 要求所有代码放在固定目录,vendor 目录管理混乱,dep 工具又不够成熟。Go Modules 的出现彻底解决了这些问题,现在已经是官方标准方案。 这篇就聊聊 Go Modules,看看它是怎么管理依赖的。 Go Modules 是什么 Go Modules 是 Go 的依赖管理系统,解决了三个核心问题。 核心功能 1// go.mod 文件示例 2module github.com/username/myproject 3 4go 1.21 5 6require ( 7 github.com/gin-gonic/gin v1.9.1 8 gorm.io/gorm v1.25.5 9) 解决的问题: 版本管理:明确指定每个依赖的版本 可重现构建:不同环境构建结果一致 依赖隔离:不同项目可以使用同一个包的不同版本 与 GOPATH 的区别 1# GOPATH 时代(痛苦) 2export GOPATH=$HOME/go 3cd $GOPATH/src/github.com/username/project 4# 所有项目共享依赖,版本冲突频繁 5 6# Go Modules 时代(简单) 7mkdir myproject 8cd myproject 9go mod init github.com/username/myproject 10# 每个项目独立管理依赖 初始化模块 创建新项目时,第一步就是初始化模块。 ...

2025-06-06 · 9 min · 1878 words · 老墨

Rust 学习笔记 15:Traits (特质)

Rust 学习笔记 15:Traits (特质) “If it walks like a duck and quacks like a duck, it must be a Trait.” 在 Go 语言中,接口 (Interface) 是隐式实现的。只要你的方法签名对上了,你就实现了接口。 在 Rust 中,Traits (特质) 必须要显式实现 (impl Trait for Type)。 1. 定义与实现 Trait 定义一个 Summary Trait: 1pub trait Summary { 2 fn summarize(&self) -> String; 3} 为 NewsArticle 和 Tweet 实现它: 1impl Summary for Tweet { 2 fn summarize(&self) -> String { 3 format!("{}: {}", self.username, self.content) 4 } 5} 2. 默认实现 (Default Implementations) Trait 中可以提供默认实现,这有点像 Java 8 的 default method。 ...

2025-05-20 · 2 min · 286 words · 老墨

Rust 学习笔记 16:生命周期 (Lifetimes)

Rust 学习笔记 16:生命周期 (Lifetimes) “To live is to suffer, to survive is to find some meaning in the suffering… of the borrow checker.” 生命周期 (Lifetimes) 是 Rust 中最令人困惑的概念,没有之一。 在 Go 或 Java 中,GC 会自动管理对象的死活。你不需要关心引用活多久,反正 GC 兜底。 在 C++ 中,你需要手动管理,稍有不慎就是 Use-After-Free。 Rust 选择了第三条路:编译期验证引用有效性。这就是生命周期检查器 (Borrow Checker) 的工作。 1. 为什么需要生命周期? 主要为了避免悬垂引用 (Dangling References)。 1{ 2 let r; 3 { 4 let x = 5; 5 r = &x; 6 } // x 在这里销毁了 7 println!("r: {}", r); // r 指向了非法内存 8} 这段代码无法通过编译,因为 r 的生命周期比 x 长。 ...

2025-05-20 · 2 min · 227 words · 老墨

Python教程32:继承

Python教程32:继承 “站在巨人的肩膀上。” 继承是面向对象编程的三大特性之一(封装、继承、多态),它让我们可以基于已有类创建新类,实现代码复用。今天我们深入学习Python的继承机制。 1. 什么是继承 问题场景 假设要创建多个类: 1# 没有继承:代码重复 2class Dog: 3 def __init__(self, name, age): 4 self.name = name 5 self.age = age 6 7 def eat(self): 8 return f"{self.name} is eating" 9 10class Cat: 11 def __init__(self, name, age): 12 self.name = name 13 self.age = age 14 15 def eat(self): 16 return f"{self.name} is eating" 17 18 def meow(self): 19 return "Meow!" 20 21# 重复的代码... 继承(Inheritance): 子类继承父类的属性和方法 子类可以添加新的属性和方法 子类可以重写父类的方法 实现代码复用,减少重复 为什么需要继承: 代码复用:共享功能写一次 逻辑清晰:体现is-a关系(狗是动物) 易于维护:修改父类,所有子类受益 扩展性强:基于现有代码扩展新功能 继承的基本语法 1class ParentClass: 2 """父类(基类、超类)""" 3 pass 4 5class ChildClass(ParentClass): 6 """子类(派生类)""" 7 pass 2. 基本继承 简单示例 1class Animal: 2 """动物类(父类)""" 3 4 def __init__(self, name, age): 5 """初始化动物""" 6 self.name = name 7 self.age = age 8 9 def eat(self): 10 """吃东西(所有动物都会吃)""" 11 return f"{self.name} is eating" 12 13 def sleep(self): 14 """睡觉""" 15 return f"{self.name} is sleeping" 16 17class Dog(Animal): 18 """狗类(子类)- 继承Animal""" 19 20 def bark(self): 21 """狗叫(狗特有的方法)""" 22 return f"{self.name} says: Woof!" 23 24class Cat(Animal): 25 """猫类(子类)- 继承Animal""" 26 27 def meow(self): 28 """猫叫(猫特有的方法)""" 29 return f"{self.name} says: Meow!" 30 31# 使用 32dog = Dog("Buddy", 3) 33print(dog.eat()) # 继承自Animal:Buddy is eating 34print(dog.bark()) # Dog自己的方法:Buddy says: Woof! 35 36cat = Cat("Whiskers", 2) 37print(cat.eat()) # 继承自Animal:Whiskers is eating 38print(cat.meow()) # Cat自己的方法:Whiskers says: Meow! 继承的特点: ...

2025-05-10 · 6 min · 1138 words · 老墨

Rust 学习笔记 14:泛型 (Generics)

Rust 学习笔记 14:泛型 (Generics) “Abstraction without overhead.” 泛型是静态类型语言提高代码复用率的关键。 在 Go 中,我们习惯了 interface{} (或 any) 带来的运行时动态检查,或者复制粘贴代码。 Rust 的泛型则不同,它在编译期通过单态化 (Monomorphization) 展开代码,运行时没有性能损耗。 1. 泛型函数 假设我们要写一个找最大值的函数,既能找 i32 也能找 char。 1fn get_largest<T: PartialOrd + Copy>(list: &[T]) -> T { 2 let mut largest = list[0]; 3 for &item in list { 4 if item > largest { // 需要 T 实现 PartialOrd 5 largest = item; 6 } 7 } 8 largest 9} 这里 T 是类型参数。<T: PartialOrd + Copy> 叫做 Trait Bound(类似 Java 的 T extends ... 或 Go 的 T interface ...)。它告诉编译器:这个 T 不是任意类型,它必须能比较大小,而且能被 Copy(为了简单起见,这里假设是栈上数据)。 ...

2025-04-26 · 2 min · 220 words · 老墨

Python教程31:类与对象基础

Python教程31:类与对象基础 “万物皆对象。” 从今天开始,我们进入Python编程的新境界——面向对象编程(OOP)。这是一种组织代码的强大范式,让程序更贴近真实世界的思维方式。 1. 什么是面向对象编程 编程范式的演进 面向过程编程(Procedural Programming): 以函数为中心 数据和操作分离 适合简单问题 1# 面向过程:管理学生信息 2students = [ 3 {"name": "Alice", "age": 20, "grade": 85}, 4 {"name": "Bob", "age": 21, "grade": 90} 5] 6 7def calculate_average(students): 8 """计算平均成绩""" 9 total = sum(s["grade"] for s in students) 10 return total / len(students) 11 12# 数据和操作分离 面向对象编程(Object-Oriented Programming, OOP): 以对象为中心 数据和操作封装在一起 更贴近真实世界建模 1# 面向对象:学生类 2class Student: 3 """学生类:数据和行为封装在一起""" 4 def __init__(self, name, age, grade): 5 self.name = name 6 self.age = age 7 self.grade = grade 8 9 def get_info(self): 10 """学生的行为""" 11 return f"{self.name}, {self.age}岁, 成绩{self.grade}" 12 13# 创建对象 14alice = Student("Alice", 20, 85) 15bob = Student("Bob", 21, 90) OOP的核心概念 类(Class): ...

2025-04-17 · 5 min · 1004 words · 老墨

GoLang教程——泛型编程入门

大家好,我是极客老墨。 Go 1.18 之前,写一个通用的 Min 函数很麻烦。想支持 int 和 float64?要么写两遍代码,要么用 interface{} 加反射,性能还差。其他语言早就有泛型了,Go 终于在 1.18 补上了这个短板。 这篇就聊聊 Go 的泛型,看看它是怎么让代码更通用、更安全的。 为什么需要泛型 没有泛型时,写通用代码很痛苦。 问题:重复代码 1// 比较两个 int 2func MinInt(a, b int) int { 3 if a < b { 4 return a 5 } 6 return b 7} 8 9// 比较两个 float64(重复代码) 10func MinFloat64(a, b float64) float64 { 11 if a < b { 12 return a 13 } 14 return b 15} 16 17// 比较两个 string(又是重复) 18func MinString(a, b string) string { 19 if a < b { 20 return a 21 } 22 return b 23} 问题:interface{} 不安全 1// 使用 interface{} 可以接受任何类型 2func Min(a, b interface{}) interface{} { 3 // 需要类型断言,容易出错 4 // 而且失去了类型安全 5} 解决方案:泛型 1// 一个函数支持多种类型 2func Min[T int | float64 | string](a, b T) T { 3 if a < b { 4 return a 5 } 6 return b 7} 泛型函数 泛型函数使用类型参数,可以处理多种类型。 ...

2025-03-22 · 9 min · 1814 words · 老墨

Python教程30:综合项目-任务管理器

Python教程30:综合项目-任务管理器 “纸上得来终觉浅,绝知此事要躬行。” 经过第三部分13课的学习,我们掌握了函数、模块、文件操作、异常处理、测试等核心知识。今天我们通过一个完整项目——命令行任务管理器,把这些知识综合应用起来。 1. 项目需求 功能需求 开发一个命令行任务管理器(Todo List),支持: 基本功能 添加任务 查看任务列表 标记任务完成 删除任务 进阶功能 任务优先级(高、中、低) 任务分类(工作、生活、学习) 截止日期 数据持久化(保存到文件) 额外功能 搜索任务 统计信息 导入/导出 技术要求 使用函数和类组织代码 模块化设计 完善的异常处理 文件操作(JSON格式存储) 单元测试 命令行交互 2. 项目结构 1task_manager/ 2├── task_manager/ # 主包 3│ ├── __init__.py 4│ ├── task.py # 任务类 5│ ├── manager.py # 管理器类 6│ ├── storage.py # 存储模块 7│ └── utils.py # 工具函数 8├── tests/ # 测试 9│ ├── __init__.py 10│ ├── test_task.py 11│ ├── test_manager.py 12│ └── test_storage.py 13├── main.py # 主程序入口 14├── README.md 15└── requirements.txt 3. 核心模块实现 3.1 任务类(task.py) 1""" 2任务类模块 3- 定义Task类表示单个任务 4- 应用:类、属性、方法 5""" 6 7from datetime import datetime 8from enum import Enum 9 10class Priority(Enum): 11 """ 12 任务优先级枚举 13 - 使用Enum定义常量 14 - 避免魔法数字 15 """ 16 LOW = 1 17 MEDIUM = 2 18 HIGH = 3 19 20class Category(Enum): 21 """任务分类""" 22 WORK = "工作" 23 LIFE = "生活" 24 STUDY = "学习" 25 OTHER = "其他" 26 27class Task: 28 """ 29 任务类 30 31 Attributes: 32 title: 任务标题 33 description: 任务描述 34 priority: 优先级 35 category: 分类 36 due_date: 截止日期 37 completed: 是否完成 38 created_at: 创建时间 39 """ 40 41 def __init__(self, title, description="", 42 priority=Priority.MEDIUM, 43 category=Category.OTHER, 44 due_date=None): 45 """ 46 初始化任务 47 48 Args: 49 title: 任务标题(必需) 50 description: 任务描述 51 priority: 优先级(默认中等) 52 category: 分类(默认其他) 53 due_date: 截止日期(datetime对象) 54 55 Raises: 56 ValueError: 标题为空时 57 """ 58 if not title: 59 raise ValueError("任务标题不能为空") 60 61 self.title = title 62 self.description = description 63 self.priority = priority 64 self.category = category 65 self.due_date = due_date 66 self.completed = False 67 self.created_at = datetime.now() 68 69 def mark_complete(self): 70 """标记任务完成""" 71 self.completed = True 72 73 def mark_incomplete(self): 74 """标记任务未完成""" 75 self.completed = False 76 77 def to_dict(self): 78 """ 79 转换为字典(用于JSON序列化) 80 81 Returns: 82 dict: 任务的字典表示 83 """ 84 return { 85 "title": self.title, 86 "description": self.description, 87 "priority": self.priority.value, 88 "category": self.category.value, 89 "due_date": self.due_date.isoformat() if self.due_date else None, 90 "completed": self.completed, 91 "created_at": self.created_at.isoformat() 92 } 93 94 @classmethod 95 def from_dict(cls, data): 96 """ 97 从字典创建任务(用于JSON反序列化) 98 99 Args: 100 data: 任务字典 101 102 Returns: 103 Task: 任务对象 104 """ 105 task = cls( 106 title=data["title"], 107 description=data.get("description", ""), 108 priority=Priority(data.get("priority", 2)), 109 category=Category(data.get("category", "其他")) 110 ) 111 112 if data.get("due_date"): 113 task.due_date = datetime.fromisoformat(data["due_date"]) 114 115 task.completed = data.get("completed", False) 116 task.created_at = datetime.fromisoformat(data["created_at"]) 117 118 return task 119 120 def __str__(self): 121 """字符串表示""" 122 status = "✓" if self.completed else " " 123 priority_symbols = { 124 Priority.LOW: "▽", 125 Priority.MEDIUM: "◇", 126 Priority.HIGH: "▲" 127 } 128 129 return f"[{status}] {priority_symbols[self.priority]} {self.title}" 130 131 def __repr__(self): 132 """开发者表示""" 133 return f"Task(title='{self.title}', completed={self.completed})" 3.2 存储模块(storage.py) 1""" 2存储模块 3- 负责任务数据的持久化 4- 应用:文件操作、JSON、异常处理 5""" 6 7import json 8import os 9from pathlib import Path 10from typing import List 11from task import Task 12 13class TaskStorage: 14 """ 15 任务存储类 16 - 使用JSON格式存储 17 - 包含完整的异常处理 18 """ 19 20 def __init__(self, file_path="tasks.json"): 21 """ 22 初始化存储 23 24 Args: 25 file_path: 存储文件路径 26 """ 27 self.file_path = Path(file_path) 28 self._ensure_file_exists() 29 30 def _ensure_file_exists(self): 31 """确保存储文件存在""" 32 if not self.file_path.exists(): 33 self.file_path.write_text("[]", encoding="utf-8") 34 35 def save_tasks(self, tasks: List[Task]): 36 """ 37 保存任务列表 38 39 Args: 40 tasks: 任务列表 41 42 Raises: 43 IOError: 文件写入失败时 44 """ 45 try: 46 data = [task.to_dict() for task in tasks] 47 48 # 使用with确保文件正确关闭 49 with open(self.file_path, "w", encoding="utf-8") as f: 50 json.dump(data, f, ensure_ascii=False, indent=2) 51 52 except Exception as e: 53 raise IOError(f"保存任务失败:{e}") 54 55 def load_tasks(self) -> List[Task]: 56 """ 57 加载任务列表 58 59 Returns: 60 List[Task]: 任务列表 61 62 Raises: 63 IOError: 文件读取失败时 64 """ 65 try: 66 with open(self.file_path, encoding="utf-8") as f: 67 data = json.load(f) 68 69 return [Task.from_dict(item) for item in data] 70 71 except json.JSONDecodeError as e: 72 raise IOError(f"JSON解析失败:{e}") 73 except Exception as e: 74 raise IOError(f"加载任务失败:{e}") 75 76 def backup(self, backup_path=None): 77 """ 78 备份任务数据 79 80 Args: 81 backup_path: 备份文件路径 82 """ 83 if backup_path is None: 84 backup_path = f"{self.file_path}.backup" 85 86 import shutil 87 shutil.copy(self.file_path, backup_path) 3.3 任务管理器(manager.py) 1""" 2任务管理器 3- 核心业务逻辑 4- 应用:类、方法、列表操作 5""" 6 7from typing import List, Optional 8from task import Task, Priority, Category 9from storage import TaskStorage 10 11class TaskManager: 12 """ 13 任务管理器 14 - 管理任务的CRUD操作 15 - 提供搜索、统计等功能 16 """ 17 18 def __init__(self, storage: TaskStorage): 19 """ 20 初始化管理器 21 22 Args: 23 storage: 存储对象 24 """ 25 self.storage = storage 26 self.tasks: List[Task] = [] 27 self.load() 28 29 def load(self): 30 """加载任务""" 31 try: 32 self.tasks = self.storage.load_tasks() 33 except IOError as e: 34 print(f"警告:{e}") 35 self.tasks = [] 36 37 def save(self): 38 """保存任务""" 39 try: 40 self.storage.save_tasks(self.tasks) 41 except IOError as e: 42 print(f"错误:{e}") 43 44 def add_task(self, task: Task): 45 """添加任务""" 46 self.tasks.append(task) 47 self.save() 48 49 def get_all_tasks(self) -> List[Task]: 50 """获取所有任务""" 51 return self.tasks 52 53 def get_task(self, index: int) -> Optional[Task]: 54 """ 55 获取指定任务 56 57 Args: 58 index: 任务索引 59 60 Returns: 61 Task或None 62 """ 63 if 0 <= index < len(self.tasks): 64 return self.tasks[index] 65 return None 66 67 def complete_task(self, index: int) -> bool: 68 """标记任务完成""" 69 task = self.get_task(index) 70 if task: 71 task.mark_complete() 72 self.save() 73 return True 74 return False 75 76 def delete_task(self, index: int) -> bool: 77 """删除任务""" 78 if 0 <= index < len(self.tasks): 79 self.tasks.pop(index) 80 self.save() 81 return True 82 return False 83 84 def search_tasks(self, keyword: str) -> List[Task]: 85 """ 86 搜索任务 87 88 Args: 89 keyword: 关键词 90 91 Returns: 92 匹配的任务列表 93 """ 94 keyword = keyword.lower() 95 return [ 96 task for task in self.tasks 97 if keyword in task.title.lower() 98 or keyword in task.description.lower() 99 ] 100 101 def get_statistics(self) -> dict: 102 """ 103 获取统计信息 104 105 Returns: 106 统计字典 107 """ 108 total = len(self.tasks) 109 completed = sum(1 for task in self.tasks if task.completed) 110 pending = total - completed 111 112 by_priority = { 113 Priority.HIGH: 0, 114 Priority.MEDIUM: 0, 115 Priority.LOW: 0 116 } 117 118 for task in self.tasks: 119 if not task.completed: 120 by_priority[task.priority] += 1 121 122 return { 123 "total": total, 124 "completed": completed, 125 "pending": pending, 126 "high_priority": by_priority[Priority.HIGH], 127 "medium_priority": by_priority[Priority.MEDIUM], 128 "low_priority": by_priority[Priority.LOW] 129 } 3.4 主程序(main.py) 1""" 2主程序入口 3- 命令行交互界面 4- 应用:函数、控制流程、异常处理 5""" 6 7from task import Task, Priority, Category 8from manager import TaskManager 9from storage import TaskStorage 10from datetime import datetime 11 12def print_menu(): 13 """打印菜单""" 14 print("\n" + "=" * 50) 15 print("任务管理器") 16 print("=" * 50) 17 print("1. 查看所有任务") 18 print("2. 添加任务") 19 print("3. 完成任务") 20 print("4. 删除任务") 21 print("5. 搜索任务") 22 print("6. 统计信息") 23 print("0. 退出") 24 print("=" * 50) 25 26def display_tasks(tasks): 27 """显示任务列表""" 28 if not tasks: 29 print("没有任务") 30 return 31 32 print("\n任务列表:") 33 for i, task in enumerate(tasks): 34 print(f"{i}. {task}") 35 if task.due_date: 36 print(f" 截止:{task.due_date.strftime('%Y-%m-%d')}") 37 38def add_task_interactive(manager: TaskManager): 39 """交互式添加任务""" 40 print("\n添加新任务") 41 42 title = input("标题:").strip() 43 if not title: 44 print("标题不能为空") 45 return 46 47 description = input("描述(可选):").strip() 48 49 print("优先级:1-低 2-中 3-高") 50 priority_input = input("选择(默认2):").strip() or "2" 51 priority = Priority(int(priority_input)) 52 53 try: 54 task = Task( 55 title=title, 56 description=description, 57 priority=priority 58 ) 59 manager.add_task(task) 60 print("✓ 任务已添加") 61 62 except ValueError as e: 63 print(f"错误:{e}") 64 65def main(): 66 """主函数""" 67 print("欢迎使用任务管理器") 68 69 # 初始化 70 storage = TaskStorage() 71 manager = TaskManager(storage) 72 73 while True: 74 print_menu() 75 choice = input("\n请选择操作:").strip() 76 77 try: 78 if choice == "1": 79 display_tasks(manager.get_all_tasks()) 80 81 elif choice == "2": 82 add_task_interactive(manager) 83 84 elif choice == "3": 85 display_tasks(manager.get_all_tasks()) 86 index = int(input("请输入任务编号:")) 87 if manager.complete_task(index): 88 print("✓ 任务已完成") 89 else: 90 print("无效的任务编号") 91 92 elif choice == "4": 93 display_tasks(manager.get_all_tasks()) 94 index = int(input("请输入任务编号:")) 95 if manager.delete_task(index): 96 print("✓ 任务已删除") 97 else: 98 print("无效的任务编号") 99 100 elif choice == "5": 101 keyword = input("请输入搜索关键词:") 102 results = manager.search_tasks(keyword) 103 display_tasks(results) 104 105 elif choice == "6": 106 stats = manager.get_statistics() 107 print(f"\n总任务数:{stats['total']}") 108 print(f"已完成:{stats['completed']}") 109 print(f"待完成:{stats['pending']}") 110 print(f" 高优先级:{stats['high_priority']}") 111 print(f" 中优先级:{stats['medium_priority']}") 112 print(f" 低优先级:{stats['low_priority']}") 113 114 elif choice == "0": 115 print("再见!") 116 break 117 118 else: 119 print("无效的选择") 120 121 except Exception as e: 122 print(f"发生错误:{e}") 123 import traceback 124 traceback.print_exc() 125 126if __name__ == "__main__": 127 main() 4. 单元测试 1# tests/test_task.py 2import unittest 3from datetime import datetime 4from task import Task, Priority, Category 5 6class TestTask(unittest.TestCase): 7 """测试Task类""" 8 9 def test_task_creation(self): 10 """测试任务创建""" 11 task = Task("测试任务") 12 13 self.assertEqual(task.title, "测试任务") 14 self.assertFalse(task.completed) 15 self.assertEqual(task.priority, Priority.MEDIUM) 16 17 def test_empty_title_raises_error(self): 18 """测试空标题抛出异常""" 19 with self.assertRaises(ValueError): 20 Task("") 21 22 def test_mark_complete(self): 23 """测试标记完成""" 24 task = Task("测试") 25 task.mark_complete() 26 27 self.assertTrue(task.completed) 28 29 def test_to_dict_and_from_dict(self): 30 """测试序列化/反序列化""" 31 task = Task("测试", priority=Priority.HIGH) 32 33 data = task.to_dict() 34 restored = Task.from_dict(data) 35 36 self.assertEqual(restored.title, task.title) 37 self.assertEqual(restored.priority, task.priority) 38 39if __name__ == "__main__": 40 unittest.main() 5. 知识点回顾 这个项目综合运用了: ...

2025-03-20 · 8 min · 1551 words · 老墨

Rust 学习笔记 13:错误处理 (Error Handling)

Rust 学习笔记 13:错误处理 (Error Handling) “To err is human; to handle it safely is Rusty.” Go 开发者最熟悉的代码可能是 if err != nil。 Rust 也有类似的机制,但它利用强大的枚举类型 Result<T, E> 把错误处理提升到了一个新的高度。 Rust 将错误分为两类: 不可恢复错误:bug 导致的,比如数组越界。使用 panic!。 可恢复错误:文件未找到、网络超时等。使用 Result<T, E>。 1. Panic! (不可恢复错误) 当程序遇到无法处理的状况时(例如访问数组越界),Rust 会 panic。程序会打印错误信息,展开 (unwind) 栈,清理数据,然后退出。 1panic!("crash and burn"); 你也可以配置 Cargo.toml 让 panic 时直接 abort(不清理栈),以减小二进制体积。 2. Result (可恢复错误) 大部分时候我们不希望程序崩溃。 1enum Result<T, E> { 2 Ok(T), 3 Err(E), 4} 处理 Result 的标准姿势是 match: 1let f = File::open("hello.txt"); 2 3let f = match f { 4 Ok(file) => file, 5 Err(error) => panic!("Problem opening the file: {:?}", error), 6}; unwrap 和 expect: 如果你确定不会出错,或者为了省事(在 demo 代码中),可以使用 unwrap()。如果出错它会直接 panic。expect("msg") 则是带自定义消息的 unwrap。 ...

2025-03-11 · 2 min · 237 words · 老墨

GoLang教程——Context上下文实战

大家好,我是极客老墨。 写并发程序时,经常遇到这样的场景:用户关闭了浏览器,但后台的数据库查询还在跑;API 调用超时了,但 Goroutine 还在等待响应。这些"失控"的 Goroutine 会浪费资源,甚至导致内存泄漏。 Go 的 Context 就是用来解决这个问题的。它能控制 Goroutine 的生命周期,实现超时、取消和数据传递。 这篇就聊聊 Context 的核心用法,看看它是怎么管理并发任务的。 Context 是什么 Context 是一个接口,定义了四个方法: 1type Context interface { 2 Deadline() (deadline time.Time, ok bool) 3 Done() <-chan struct{} 4 Err() error 5 Value(key interface{}) interface{} 6} 核心功能: 取消信号:通知 Goroutine 停止工作 超时控制:限制任务执行时间 数据传递:在调用链中传递元数据 创建 Context Go 提供了几个函数来创建 Context。 Background 和 TODO 1import "context" 2 3// Background:根 Context,通常在 main 函数中使用 4ctx := context.Background() 5 6// TODO:当不确定用什么 Context 时使用 7ctx := context.TODO() 要点: ...

2025-03-02 · 7 min · 1481 words · 老墨

[GoLang避坑实战-14] 写得爽,跑得快:表格驱动测试的工程级避坑实战

大家好,我是极客老墨! 刚转 Go 的时候,我还在找"Go 版的 JUnit 在哪"。结果发现,Go 根本不需要第三方测试框架,go test 命令 + testing 包就够了。 写测试不只是为了完成 KPI,更是为了**“让自己晚上能睡个好觉”**。这篇我们不仅聊基础,更要聊聊在大厂工程实践中,如何写出既稳健又专业的测试。 Go中的测试,不是"一等公民",是"超等公民"。Go 编译器自带 go test 工具,标准库提供 testing 包,写测试简单到爆。 1. 单元测试:从入门到专业 Go 的测试文件规则很简单:以 _test.go 结尾,函数名以 Test 开头。 1.1 基础写法 1func TestAdd(t *testing.T) { 2 if got := Add(1, 2); got != 3 { 3 t.Errorf("Add(1, 2) = %d; want 3", got) 4 } 5} 跑一下: 1$ go test -v 2=== RUN TestAdd 3--- PASS: TestAdd (0.00s) 4PASS 5ok example.com/math 0.392s 就这么简单。不需要装任何库,不需要配置文件。 ...

2025-02-19 · 3 min · 491 words · 老墨

Rust 学习笔记 12:常用集合 (Collections)

Rust 学习笔记 12:常用集合 (Collections) “Data structures are the backbone of any program.” Rust 标准库提供了一系列非常实用的集合数据结构。和数组/元组不同,它们的数据存储在堆 (Heap) 上,这意味着长度可以动态变化。 如果你有 Go 语言背景,可以这样类比: Vec<T> ≈ Slice []T String ≈ string (但在 Rust 中它是可变的) HashMap<K, V> ≈ Map map[K]V 1. Vector (动态数组) Vector 是最常用的集合。 1let mut v: Vec<i32> = Vec::new(); 2v.push(1); 3v.push(2); 4v.push(3); 或者使用 vec! 宏: 1let v = vec![1, 2, 3]; 访问元素: 有两种方式,一种是索引 &v[2](越界会 Panic),一种是 v.get(2)(返回 Option<&T>)。在 Rust 中,推荐尽可能用 get 来安全处理越界。 借用规则陷阱: 当你持有一个元素的引用时,不能往 Vector 里 push 新元素! 原因:push 可能会导致 vector 扩容(重新分配内存),如果你手里还攥着旧内存的引用,那就是悬垂指针了。Rust 编译器会死死盯着你。 ...

2025-02-18 · 2 min · 253 words · 老墨

Python教程29:函数与模块总结

Python教程29:函数与模块总结 “温故而知新,可以为师矣。” 经过前面13课的学习(第17-28课),我们系统学习了Python的函数、模块、异常处理等核心概念。今天我们回顾总结,梳理知识体系,巩固所学内容。 1. 函数基础回顾 核心概念 函数的作用: 代码复用:写一次,用多次 模块化:分解复杂问题 抽象:隐藏实现细节 可读性:函数名即文档 关键知识点 1# 基本结构 2def function_name(parameters): 3 """文档字符串""" 4 # 函数体 5 return result 6 7# 参数类型 8def func(a, b=默认值, *args, **kwargs): 9 """ 10 a: 位置参数 11 b: 默认参数 12 *args: 可变位置参数(元组) 13 **kwargs: 可变关键字参数(字典) 14 """ 15 pass 16 17# 返回值 18def multi_return(): 19 return value1, value2 # 实际返回元组 20 21# 函数是一等公民 22def outer(func): 23 return func # 可以作为参数和返回值 回顾第17课:函数基础(定义、参数、返回值、作用域) 2. Lambda与高阶函数 Lambda表达式 1# 匿名函数 2square = lambda x: x ** 2 3 4# 适用场景:简单、临时、作为参数 5sorted(students, key=lambda x: x["score"]) 高阶函数 1# map - 映射 2squares = map(lambda x: x**2, numbers) 3 4# filter - 过滤 5evens = filter(lambda x: x % 2 == 0, numbers) 6 7# reduce - 累积(需要从functools导入) 8from functools import reduce 9total = reduce(lambda x, y: x + y, numbers) 何时用Lambda vs普通函数: ...

2025-02-16 · 5 min · 910 words · 老墨

Python教程28:单元测试基础

Python教程28:单元测试基础 “测试不是万能的,但没有测试是万万不能的。” 代码写完了但不敢重构?担心修改一处就影响全局?单元测试能给你信心。今天我们学习Python的unittest模块,掌握自动化测试的基础。 1. 什么是单元测试 测试的重要性 为什么需要测试: 验证代码的正确性 防止回归(修改代码导致原有功能失效) 提高代码质量 便于重构(有测试保障) 作为文档(测试展示如何使用函数) 单元测试(Unit Test): 测试最小可测试单元(函数、方法、类) 独立运行,互不影响 快速执行 自动化 测试示例 待测试的代码: 1# calculator.py 2def add(a, b): 3 """加法""" 4 return a + b 5 6def divide(a, b): 7 """除法""" 8 if b == 0: 9 raise ValueError("除数不能为零") 10 return a / b 手动测试(不推荐): 1# 手动测试,繁琐且不可重复 2print(add(2, 3)) # 应该是5 3print(divide(10, 2)) # 应该是5 4# 每次修改代码都要手动运行... 自动化测试(推荐): 1# test_calculator.py 2import unittest 3from calculator import add, divide 4 5class TestCalculator(unittest.TestCase): 6 """ 7 测试用例类 8 - 继承unittest.TestCase 9 - 测试方法以test_开头 10 - 使用断言验证结果 11 """ 12 13 def test_add(self): 14 """测试加法""" 15 self.assertEqual(add(2, 3), 5) 16 self.assertEqual(add(-1, 1), 0) 17 18 def test_divide(self): 19 """测试除法""" 20 self.assertEqual(divide(10, 2), 5) 21 22 with self.assertRaises(ValueError): 23 divide(10, 0) 24 25if __name__ == "__main__": 26 unittest.main() 运行测试: ...

2025-02-07 · 5 min · 1059 words · 老墨

Rust 学习笔记 11:包与模块 (Packages & Modules)

Rust 学习笔记 11:包与模块 (Packages & Modules) “Organization is what you do before you do it, so that when you do it, it’s not all mixed up.” – A. A. Milne 欢迎来到 2025 年! 在完成了前 10 章的基础语法学习后,我们现在的 Rust 代码全都写在一个 main.rs 里。这对于写个猜数字游戏还行,但要写大项目,文件组织是必修课。 Rust 的模块系统 (Module System) 有点复杂,甚至被称为"新手劝退三大难"之一(另外俩是所有权和生命周期)。 1. 家族谱系:Package, Crate, Module 首先要分清三个概念: Package (包):Cargo 的功能单元。包含 Cargo.toml。一个 Package 可以包含多个 Binary Crate,但只能有一个 Library Crate。 Crate (箱):编译单元。main.rs 是二进制 Crate 的根,lib.rs 是库 Crate 的根。 Module (模块):代码组织单元。用 mod 关键字定义。 层级关系:Package -> (contains) -> Crates -> (contains) -> Modules。 ...

2025-02-06 · 2 min · 243 words · 老墨

Python教程27:上下文管理器与with语句

Python教程27:上下文管理器与with语句 “始有终,开必合。” 我们已经多次使用with语句打开文件,今天深入学习它的原理——上下文管理器(Context Manager)。这是Python资源管理的利器,让代码更安全、更优雅。 1. 什么是上下文管理器 问题场景 资源(文件、网络连接、数据库连接、锁)需要正确管理: 1# 问题代码:可能忘记关闭 2file = open("data.txt") 3content = file.read() 4file.close() # 忘记关闭?异常时未关闭? 5 6# try-finally保证关闭 7file = open("data.txt") 8try: 9 content = file.read() 10finally: 11 file.close() # 总是执行 12 13# 但每次都这样写很繁琐... 上下文管理器(Context Manager): 自动管理资源的生命周期 确保资源的正确获取和释放 使用with语句 即使发生异常也能正确清理 1# with语句:简洁且安全 2with open("data.txt") as file: 3 content = file.read() 4# 自动关闭文件,即使有异常也会关闭 with语句的优势 自动清理:离开with块自动释放资源 异常安全:即使发生异常也会执行清理 代码简洁:不需要显式的try-finally 可读性强:明确表达资源管理意图 2. 上下文管理器协议 上下文管理器需要实现两个魔术方法: enter() 进入with块时调用,返回资源对象: 1def __enter__(self): 2 """ 3 进入上下文时调用 4 - 获取资源 5 - 返回的对象赋给as后的变量 6 """ 7 return self # 或其他资源对象 exit() 离开with块时调用,执行清理操作: ...

2025-01-26 · 5 min · 877 words · 老墨

[GoLang避坑实战-13] 标准库挖矿:这几个库包能让你少写一半代码

大家好,我是极客老墨。 Go 标准库是个宝藏。fmt 格式化输出、strings 字符串处理、time 时间操作、json 序列化、os 文件读写,这些日常开发 80% 的需求都能搞定,不用到处找第三方库。 今天精选几个最常用的包,看看它们怎么用,能解决什么问题。 fmt:格式化输入输出 fmt 包用于格式化输出和输入,是最常用的包之一。 基本输出 1import "fmt" 2 3func main() { 4 // 输出不换行 5 fmt.Print("Hello") 6 fmt.Print("World") 7 8 // 输出换行 9 fmt.Println("Hello") 10 fmt.Println("World") 11 12 // 格式化输出 13 name := "Go" 14 age := 15 15 fmt.Printf("Name: %s, Age: %d\n", name, age) 16} 常用占位符 1// 字符串 2fmt.Printf("%s\n", "hello") // hello 3fmt.Printf("%q\n", "hello") // "hello" (带引号) 4 5// 整数 6fmt.Printf("%d\n", 42) // 42 (十进制) 7fmt.Printf("%b\n", 42) // 101010 (二进制) 8fmt.Printf("%x\n", 42) // 2a (十六进制) 9 10// 浮点数 11fmt.Printf("%f\n", 3.14) // 3.140000 12fmt.Printf("%.2f\n", 3.14159) // 3.14 (保留2位) 13 14// 布尔值 15fmt.Printf("%t\n", true) // true 16 17// 通用 18fmt.Printf("%v\n", 42) // 42 (默认格式) 19fmt.Printf("%+v\n", struct{X int}{42}) // {X:42} (带字段名) 20fmt.Printf("%#v\n", []int{1,2}) // []int{1, 2} (Go语法) 21fmt.Printf("%T\n", 42) // int (类型) 格式化字符串 1// Sprintf 返回字符串 2msg := fmt.Sprintf("Hello, %s!", "World") 3fmt.Println(msg) // Hello, World! 4 5// Errorf 创建错误 6err := fmt.Errorf("failed to open file: %s", "config.json") 要点: ...

2025-01-12 · 14 min · 2878 words · 老墨

Python教程26:异常处理

Python教程26:异常处理 “预则立,不预则废。” 程序运行时难免会遇到错误:文件不存在、网络断开、用户输入非法数据。异常处理机制让程序能够优雅地应对这些错误,而不是直接崩溃。今天我们学习Python的异常处理。 1. 什么是异常 错误vs异常 语法错误(Syntax Error): 代码写错了,Python无法解析 程序无法运行 例如:忘记冒号、括号不匹配 1# 语法错误示例 2if True # SyntaxError: 缺少冒号 3 print("Hello") 异常(Exception): 语法正确,但运行时出错 可以被捕获和处理 例如:除零、文件不存在、类型错误 1# 异常示例 2x = 1 / 0 # ZeroDivisionError: division by zero 为什么需要异常处理 没有异常处理: 1def divide(a, b): 2 return a / b 3 4result = divide(10, 0) # 程序崩溃! 5print("这行代码不会执行") 有异常处理: 1def divide(a, b): 2 try: 3 return a / b 4 except ZeroDivisionError: 5 print("错误:除数不能为0") 6 return None 7 8result = divide(10, 0) 9print("程序继续运行") # 这行会执行 异常处理的作用: 防止程序崩溃 提供友好的错误信息 执行清理操作(关闭文件、释放资源) 记录错误日志 优雅降级(功能不可用时提供备选方案) 2. 基本语法:try-except 1try: 2 # 可能出错的代码 3 risky_operation() 4except ExceptionType: 5 # 处理异常的代码 6 handle_error() 简单示例 1try: 2 num = int(input("请输入数字:")) 3 result = 10 / num 4 print(f"结果:{result}") 5except ValueError: 6 print("错误:请输入有效的数字") 7except ZeroDivisionError: 8 print("错误:除数不能为零") 执行流程: ...

2025-01-06 · 5 min · 931 words · 老墨

Python教程25:文件操作基础

Python教程25:文件操作基础 “磁盘是内存的延伸,文件是数据的归宿。” 程序经常需要读取配置文件、处理日志、保存数据。今天我们学习Python的文件操作,掌握数据持久化的基本技能。 1. 文件操作概述 为什么需要文件操作 内存vs文件: 内存(RAM):程序运行时的数据存储,速度快但程序结束后数据消失 文件(磁盘):永久存储数据,程序结束后数据保留 文件操作的应用场景: 读取配置文件(config.ini、settings.json) 处理日志文件(分析服务器日志) 数据持久化(保存用户数据) 批量处理(处理大量CSV、Excel文件) 网络爬虫(保存爬取的数据) Python文件操作的三个步骤 1# 1. 打开文件 2file = open("example.txt", "r") 3 4# 2. 读取/写入文件 5content = file.read() 6 7# 3. 关闭文件 8file.close() 为什么要关闭文件: 释放系统资源 确保数据完全写入磁盘 避免文件被锁定 防止内存泄漏 2. 打开文件:open()函数 open()是Python的内置函数,用于打开文件: 1# open(file, mode='r', encoding=None) 2# - file: 文件路径 3# - mode: 打开模式 4# - encoding: 编码格式(文本文件需要指定) 5 6file = open("data.txt", "r", encoding="utf-8") 文件模式 模式 说明 文件必须存在 覆盖内容 'r' 只读(默认) 是 - 'w' 只写 否(自动创建) 是 'a' 追加 否(自动创建) 否 'x' 独占创建 否(已存在报错) - 'r+' 读写 是 否 'w+' 读写 否(自动创建) 是 'a+' 读写追加 否(自动创建) 否 二进制模式:在模式后加'b' ...

2024-12-26 · 4 min · 827 words · 老墨

Rust 学习笔记 10:项目实战一:猜数字

Rust 学习笔记 10:项目实战一:猜数字 “Programming is not about typing, it’s about thinking.” 之前的 9 篇笔记,我们一直在抠语法细节。 今天是圣诞节(假设),我们来点轻松的:用 Rust 写一个猜数字游戏。 规则很简单: 程序生成一个 1~100 的随机数。 玩家输入猜测。 程序提示 “大了”、“小了” 或 “猜对了”。 猜对了游戏结束,否则继续。 1. 引入依赖 Crates Rust 的标准库很精简,没有内置随机数生成。我们需要引入第三方库 rand。 修改 Cargo.toml: 1[dependencies] 2rand = "0.9" 这和 Go 的 go get 不太一样,Rust 更像是 Node.js 的 npm,依赖管理非常现代化。 2. 获取用户输入 1use std::io; 2 3let mut guess = String::new(); // 创建可变空字符串 4 5io::stdin() 6 .read_line(&mut guess) // 传可变引用进去修改 7 .expect("Failed to read line"); // 处理 Result::Err 注意那个 read_line 返回的是 io::Result 枚举。如果读取失败,它会返回 Err,expect 方法会帮你 panic 并打印错误信息。在 Rust 中,忽略返回值是会被警告的。 ...

2024-12-25 · 2 min · 249 words · 老墨

GoLang教程——并发进阶

大家好,我是极客老墨。 并发编程中,Channel 很好用,但不是万能的。有时候需要更精细的控制:等待一组任务完成、保护共享数据、限制并发数量。这时候就需要 sync 包的同步工具了。 这篇就聊聊 Go 的并发进阶工具,看看它们各自适合什么场景。 WaitGroup:等待组 WaitGroup 用于等待一组 Goroutine 完成,是最常用的同步工具。 基本用法 1import ( 2 "fmt" 3 "sync" 4 "time" 5) 6 7func worker(id int, wg *sync.WaitGroup) { 8 defer wg.Done() // 完成时调用 9 10 fmt.Printf("Worker %d starting\n", id) 11 time.Sleep(time.Second) 12 fmt.Printf("Worker %d done\n", id) 13} 14 15func main() { 16 var wg sync.WaitGroup 17 18 for i := 1; i <= 5; i++ { 19 wg.Add(1) // 增加计数 20 go worker(i, &wg) 21 } 22 23 wg.Wait() // 等待所有完成 24 fmt.Println("All workers completed") 25} 要点: ...

2024-12-05 · 9 min · 1714 words · 老墨

Rust 学习笔记 09:枚举与模式匹配

Rust 学习笔记 09:枚举与模式匹配 “Null References: The Billion Dollar Mistake.” – Tony Hoare Go 语言没有枚举(只有 iota 常量),也没有代数数据类型,更没有模式匹配。所以这一章对于 Go 开发者来说,是全新的世界。 1. 它是枚举,但又不仅仅是枚举 在 C/Go 中,Enum 通常只是整数的别名。 但在 Rust 中,Enum 可以携带数据,而且每个变体携带的数据类型可以不同。 1enum Message { 2 Quit, // 没有数据 3 Move { x: i32, y: i32 }, // 包含匿名结构体 4 Write(String), // 包含 String 5 ChangeColor(i32, i32, i32), // 包含元组 6} 这一个 Enum 类型,就涵盖了消息系统的所有可能性。这比 Go 中定义一堆 struct 加上 interface 要简洁直观得多。 2. Option 枚举:告别 Null Rust 没有 null。 那我想要表达"变量可能为空"怎么办? Rust 标准库提供了一个枚举 Option<T>: ...

2024-11-30 · 2 min · 224 words · 老墨

Python教程24:内置函数深入

Python教程24:内置函数深入 “工欲善其事,必先利其器。” Python提供了60多个内置函数(built-in functions),它们无需导入即可使用。今天我们深入学习最常用、最实用的内置函数,让你的代码更简洁高效。 1. 什么是内置函数 内置函数(Built-in Functions): Python解释器自带的函数 无需import即可直接使用 用C语言实现,性能优异 覆盖最常见的编程需求 查看所有内置函数: 1# dir(__builtins__)会列出所有内置对象 2# __builtins__是Python内置命名空间 3import builtins 4print(dir(builtins)) 5 6# 或者查看官方文档 7help(builtins) 我们已经用过很多内置函数:print()、len()、type()、int()、str()等。 2. 类型转换函数 基础类型转换 1# int() - 转换为整数 2# Python内置函数,将其他类型转为整数 3print(int("123")) # 123 4print(int(3.14)) # 3(截断小数部分) 5print(int("1010", 2)) # 10(二进制转十进制) 6print(int("FF", 16)) # 255(十六进制转十进制) 7 8# float() - 转换为浮点数 9print(float("3.14")) # 3.14 10print(float("inf")) # inf(无穷大) 11 12# str() - 转换为字符串 13print(str(123)) # "123" 14print(str([1, 2, 3])) # "[1, 2, 3]" 15 16# bool() - 转换为布尔值 17# 遵循Python的真值规则 18print(bool(0)) # False 19print(bool("")) # False 20print(bool([])) # False 21print(bool("False")) # True(非空字符串为True) 容器类型转换 1# list() - 转换为列表 2print(list("Python")) # ['P', 'y', 't', 'h', 'o', 'n'] 3print(list(range(5))) # [0, 1, 2, 3, 4] 4print(list({1, 2, 3})) # [1, 2, 3] 5 6# tuple() - 转换为元组 7print(tuple([1, 2, 3])) # (1, 2, 3) 8print(tuple("abc")) # ('a', 'b', 'c') 9 10# set() - 转换为集合(自动去重) 11print(set([1, 2, 2, 3, 3, 3])) # {1, 2, 3} 12print(set("hello")) # {'h', 'e', 'l', 'o'} 13 14# dict() - 转换为字典 15print(dict([('a', 1), ('b', 2)])) # {'a': 1, 'b': 2} 16print(dict(a=1, b=2)) # {'a': 1, 'b': 2} 3. 数学函数 1# abs() - 绝对值 2print(abs(-10)) # 10 3print(abs(-3.14)) # 3.14 4 5# round() - 四舍五入 6# 可以指定保留几位小数 7print(round(3.14159)) # 3 8print(round(3.14159, 2)) # 3.14 9print(round(3.14159, 3)) # 3.142 10 11# pow() - 幂运算 12# pow(x, y) 等价于 x ** y 13# pow(x, y, z) 等价于 (x ** y) % z(高效) 14print(pow(2, 3)) # 8 15print(pow(2, 3, 5)) # 3(即 8 % 5) 16 17# sum() - 求和 18# 可以指定起始值 19print(sum([1, 2, 3, 4, 5])) # 15 20print(sum([1, 2, 3], 10)) # 16(10 + 1 + 2 + 3) 21 22# min() / max() - 最小值/最大值 23print(min(1, 2, 3, 4, 5)) # 1 24print(max([1, 2, 3, 4, 5])) # 5 25print(min("apple", "banana")) # "apple"(字典序) 26 27# divmod() - 同时返回商和余数 28print(divmod(17, 5)) # (3, 2) 4. 序列操作函数 all() 和 any() 1#all() - 所有元素为True时返回True 2# Python内置函数,用于检查可迭代对象中所有元素 3print(all([True, True, True])) # True 4print(all([True, False, True])) # False 5print(all([])) # True(空序列) 6 7# 实际应用:检查所有数字是否为正数 8numbers = [1, 2, 3, 4, 5] 9print(all(n > 0 for n in numbers)) # True 10 11# any() - 任一元素为True时返回True 12print(any([False, False, True])) # True 13print(any([False, False, False])) # False 14print(any([])) # False(空序列) 15 16# 实际应用:检查是否含有偶数 17print(any(n % 2 == 0 for n in numbers)) # True sorted() 和 reversed() 1# sorted() - 排序(返回新列表) 2# 不修改原序列,返回排序后的新列表 3numbers = [3, 1, 4, 1, 5, 9, 2, 6] 4print(sorted(numbers)) # [1, 1, 2, 3, 4, 5, 6, 9] 5print(sorted(numbers, reverse=True)) # [9, 6, 5, 4, 3, 2, 1, 1] 6 7# 按长度排序 8words = ["python", "is", "awesome"] 9print(sorted(words, key=len)) # ['is', 'python', 'awesome'] 10 11# 按复杂规则排序 12students = [ 13 {"name": "Alice", "score": 85}, 14 {"name": "Bob", "score": 92}, 15 {"name": "Charlie", "score": 78} 16] 17print(sorted(students, key=lambda x: x["score"], reverse=True)) 18 19# reversed() - 反转(返回迭代器) 20print(list(reversed([1, 2, 3]))) # [3, 2, 1] 21print(list(reversed("Python"))) # ['n', 'o', 'h', 't', 'y', 'P'] zip() 和 enumerate() 1# zip() - 并行打包多个序列 2# Python内置函数,用于同时遍历多个序列 3names = ["Alice", "Bob", "Charlie"] 4ages = [25, 30, 35] 5cities = ["Beijing", "Shanghai", "Guangzhou"] 6 7# 打包成元组列表 8pairs = list(zip(names, ages, cities)) 9print(pairs) 10# [('Alice', 25, 'Beijing'), ('Bob', 30, 'Shanghai'), ('Charlie', 35, 'Guangzhou')] 11 12# 解包:zip的逆操作 13names2, ages2, cities2 = zip(*pairs) 14print(names2) # ('Alice', 'Bob', 'Charlie') 15 16# enumerate() - 带索引遍历 17# 返回(索引, 值)的元组 18for i, fruit in enumerate(["apple", "banana", "cherry"]): 19 print(f"{i}: {fruit}") 20 21# 指定起始索引 22for i, fruit in enumerate(["apple", "banana"], start=1): 23 print(f"{i}. {fruit}") 5. 高阶函数 map() 1# map() - 映射函数到序列 2# apply函数到每个元素,返回迭代器 3numbers = [1, 2, 3, 4, 5] 4 5# 平方 6squares = map(lambda x: x**2, numbers) 7print(list(squares)) # [1, 4, 9, 16, 25] 8 9# 字符串转大写 10words = ["hello", "world", "python"] 11upper_words = map(str.upper, words) 12print(list(upper_words)) # ['HELLO', 'WORLD', 'PYTHON'] 13 14# 多个序列 15a = [1, 2, 3] 16b = [4, 5, 6] 17sums = map(lambda x, y: x + y, a, b) 18print(list(sums)) # [5, 7, 9] filter() 1# filter() - 过滤序列 2# 保留使函数返回True的元素 3numbers = range(1, 11) 4 5# 过滤出偶数 6evens = filter(lambda x: x % 2 == 0, numbers) 7print(list(evens)) # [2, 4, 6, 8, 10] 8 9# 过滤空字符串 10strings = ["hello", "", "world", "", "python"] 11non_empty = filter(None, strings) # None会过滤掉假值 12print(list(non_empty)) # ['hello', 'world', 'python'] 6. 对象和属性函数 1# id() - 对象的唯一标识(内存地址) 2# Python内置函数,返回对象的身份标识 3a = [1, 2, 3] 4b = [1, 2, 3] 5print(id(a)) # 140234567890123(示例) 6print(id(b)) # 140234567890456(不同) 7print(id(a) == id(b)) # False 8 9# isinstance() - 类型检查 10print(isinstance(42, int)) # True 11print(isinstance("hello", str)) # True 12print(isinstance([1, 2], (list, tuple))) # True(多个类型) 13 14# hasattr() / getattr() / setattr() - 属性操作 15class Person: 16 def __init__(self, name): 17 self.name = name 18 19p = Person("Alice") 20print(hasattr(p, "name")) # True 21print(getattr(p, "name")) # "Alice" 22print(getattr(p, "age", 0)) # 0(默认值) 23setattr(p, "age", 25) 24print(p.age) # 25 25 26# dir() - 列出对象的所有属性和方法 27print(dir(str)) # 列出字符串的所有方法 7. 输入输出函数 1# print() -输出 2# sep参数:分隔符 3print("a", "b", "c", sep="-") # a-b-c 4 5# end参数:结束符(默认是换行) 6print("Hello", end=" ") 7print("World") # Hello World 8 9# file参数:输出到文件 10with open("output.txt", "w") as f: 11 print("Hello", file=f) 12 13# input() - 获取用户输入 14# 返回字符串,需要转换类型 15name = input("请输入姓名:") 16age = int(input("请输入年龄:")) 8. 编译和执行函数 1# eval() - 执行Python表达式字符串 2# 警告:不要对不可信的输入使用eval()! 3result = eval("1 + 2 * 3") 4print(result) # 7 5 6# exec() - 执行Python代码字符串 7code = """ 8def greet(name): 9 return f"Hello, {name}" 10print(greet("Alice")) 11""" 12exec(code) # 输出:Hello, Alice 13 14# compile() - 编译代码 15# 用于需要多次执行同一代码的场景 16code_obj = compile("1 + 2", "<string>", "eval") 17print(eval(code_obj)) # 3 安全警告:eval()和exec()很危险,不要对用户输入使用! ...

2024-11-22 · 5 min · 993 words · 老墨

[GoLang避坑实战-12] 并发初体验:Goroutine 和 Channel 真的那么神吗?

大家好,我是极客老墨。 传统语言里写并发,要创建线程、加锁、处理竞态条件,一不小心就死锁。Go 的并发模型完全不同:用 Goroutine 代替线程,用 Channel 代替锁。这种 CSP(通信顺序进程)模型,让并发编程变得简单多了。 这篇就聊聊 Go 的并发基础,看看 Goroutine 和 Channel 是怎么配合工作的。 Goroutine 基础 Goroutine 是 Go 的轻量级协程,比线程轻量得多。 启动 Goroutine 使用 go 关键字启动一个 Goroutine。 1package main 2 3import ( 4 "fmt" 5 "time" 6) 7 8func sayHello() { 9 fmt.Println("Hello from goroutine") 10} 11 12func main() { 13 // 启动一个 Goroutine 14 go sayHello() 15 16 // 主 Goroutine 继续执行 17 fmt.Println("Hello from main") 18 19 // 等待一下,否则程序会立即退出 20 time.Sleep(time.Second) 21} 要点: ...

2024-11-19 · 13 min · 2588 words · 老墨

[GoLang避坑实战-10] 不要 panic!Go 风格错误处理的"优雅避坑"指南

大家好,我是极客老墨。 写代码时,错误处理往往占了一半的工作量。文件打不开、网络连不上、数据格式不对,这些都是常态。Go 的错误处理很直接:错误就是一个返回值,你必须显式检查它。 这篇就聊聊 Go 的错误处理机制,看看它是怎么让代码更健壮的。 错误是值 Go 的核心理念:错误是值(Errors are values),不是异常。 error 接口 Go 内置的 error 是一个接口,只有一个方法: 1type error interface { 2 Error() string 3} 任何实现了 Error() string 方法的类型都是 error。 创建错误 1import "errors" 2 3// 方式 1:使用 errors.New 4err1 := errors.New("something went wrong") 5 6// 方式 2:使用 fmt.Errorf(支持格式化) 7err2 := fmt.Errorf("failed to open file: %s", filename) 8 9// 方式 3:自定义错误类型 10type MyError struct { 11 Code int 12 Msg string 13} 14 15func (e MyError) Error() string { 16 return fmt.Sprintf("error %d: %s", e.Code, e.Msg) 17} 要点: ...

2024-10-29 · 10 min · 2103 words · 老墨

Python教程23:迭代器(Iterator)

Python教程23:迭代器(Iterator) “万变不离其宗。” 我们已经用过很多次for循环,但你知道它背后的机制吗?今天我们学习迭代器(Iterator)——Python迭代的核心协议。 1. 什么是迭代器 可迭代对象vs迭代器 可迭代对象(Iterable): 可以用for循环遍历的对象 实现了__iter__()方法 例如:列表、元组、字典、字符串、生成器 迭代器(Iterator): 实现了__iter__()和__next__()方法的对象 可以被next()函数调用 记住当前位置,可以逐个返回元素 关系: 所有迭代器都是可迭代对象 但不是所有可迭代对象都是迭代器 可迭代对象调用iter()得到迭代器 1# 列表是可迭代对象,但不是迭代器 2my_list = [1, 2, 3] 3print(hasattr(my_list, '__iter__')) # True 4print(hasattr(my_list, '__next__')) # False 5 6# 获取迭代器 7iterator = iter(my_list) 8print(hasattr(iterator, '__iter__')) # True 9print(hasattr(iterator, '__next__')) # True 10 11# 使用迭代器 12print(next(iterator)) # 1 13print(next(iterator)) # 2 14print(next(iterator)) # 3 15# print(next(iterator)) # StopIteration 2. 迭代器协议 Python的迭代器协议包含两个方法: iter() 返回迭代器对象本身: 1class MyIterator: 2 def __iter__(self): 3 return self # 返回自己 作用: 让对象可以用于for循环 让对象可以调用iter() next() 返回下一个元素,没有元素时抛出StopIteration: 1class MyIterator: 2 def __next__(self): 3 # 返回下一个元素 4 # 或抛出StopIteration 5 pass 3. for循环的工作原理 for循环实际上是迭代器协议的语法糖: ...

2024-10-27 · 4 min · 797 words · 老墨

Rust 学习笔记 08:结构体 (Structs)

Rust 学习笔记 08:结构体 (Structs) “Structure is the maker of light.” – Louis Kahn 在 Go 中,我们用 struct 来组织数据,用 func (s *Struct) Method() 来定义方法。 在 Rust 中,这种感觉非常相似,但细节决定成败。 1. 定义与实例化 定义一个 User 结构体: 1struct User { 2 active: bool, 3 username: String, 4 email: String, 5 sign_in_count: u64, 6} 这和 Go 几乎一模一样(除了字段名为 snake_case,Go 推荐 CamelCase)。 实例化: 1let user1 = User { 2 email: String::from("someone@example.com"), 3 username: String::from("someusername123"), 4 active: true, 5 sign_in_count: 1, 6}; 区别点:如果想修改 user1 的字段,必须把 user1 声明为 mut。Rust 不允许仅把某个字段标记为可变,可变性是针对整个实例的。 ...

2024-10-15 · 2 min · 342 words · 老墨

Python教程22:生成器(Generator)

Python教程22:生成器(Generator) “按需而生,用时即弃。” 生成器是Python中一个强大但常被忽视的特性。它能让你处理超大数据集而不耗尽内存,今天我们深入学习生成器的原理和应用。 1. 什么是生成器 问题场景 假设要生成1亿个数字: 1# 方法1:列表(占用大量内存) 2numbers = [i for i in range(100000000)] 3# 内存占用:约800MB! 4 5# 方法2:生成器(几乎不占内存) 6numbers = (i for i in range(100000000)) 7# 内存占用:几百字节 生成器(Generator): 一种特殊的迭代器 惰性求值:需要时才计算,不提前生成所有值 内存高效:一次只保存一个值 使用yield关键字或生成器表达式创建 为什么需要生成器: 处理大数据集(GB/TB级别) 无限序列(斐波那契数列、素数序列) 管道式数据处理 节省内存 2. 生成器函数 使用yield 普通函数用return返回值,生成器函数用yield: 1def simple_generator(): 2 """ 3 生成器函数示例 4 - yield关键字使函数变成生成器 5 - 每次yield都会"暂停"函数,保存状态 6 - 下次调用时从暂停处继续 7 """ 8 print("开始") 9 yield 1 10 print("继续") 11 yield 2 12 print("再继续") 13 yield 3 14 print("结束") 15 16# 创建生成器对象(还没执行函数体) 17gen = simple_generator() 18print(type(gen)) # <class 'generator'> 19 20# 每次调用next()执行到下一个yield 21print(next(gen)) # 输出:开始 1 22print(next(gen)) # 输出:继续 2 23print(next(gen)) # 输出:再继续 3 24# print(next(gen)) # StopIteration异常 yield的执行流程: ...

2024-10-14 · 4 min · 767 words · 老墨

Python教程21:包(Package)

Python教程21:包(Package) “治大国如烹小鲜,理大项目如分包装。” 上一课我们学习了模块,今天更进一步,学习包(Package)——模块的集合。当项目规模变大时,包能帮你更好地组织代码。 1. 什么是包 定义 **包(Package)**是一个包含__init__.py文件的目录,用于组织相关的模块。 为什么需要包: 层次化组织:大项目有上百个模块,需要分类 命名空间:不同包可以有同名模块 代码复用:打包分发给他人使用 团队协作:不同团队负责不同包 简单示例 项目结构: 1myproject/ 2├── main.py 3└── utils/ # 这是一个包 4 ├── __init__.py # 必需!标识这是一个包 5 ├── string_utils.py 6 └── math_utils.py init.py的作用: 告诉Python这个目录是一个包 可以为空文件 也可以包含包的初始化代码 Python 3.3+可以省略(但不推荐) 使用包: 1# main.py 2from utils import string_utils 3from utils import math_utils 4 5# 或者 6import utils.string_utils 7import utils.math_utils 2. 创建第一个包 步骤 创建目录结构: 1mymath/ 2├── __init__.py 3├── basic.py 4└── advanced.py 编写模块代码: 1# mymath/basic.py 2"""基础数学运算""" 3 4def add(a, b): 5 """加法""" 6 return a + b 7 8def subtract(a, b): 9 """减法""" 10 return a - b 1# mymath/advanced.py 2"""高级数学运算""" 3 4def power(base, exp): 5 """幂运算""" 6 return base ** exp 7 8def sqrt(x): 9 """平方根(简单实现)""" 10 return x ** 0.5 配置__init__.py: 1# mymath/__init__.py 2""" 3mymath包:提供数学运算功能 4 5这个文件在包被导入时执行,可以用来: 61. 初始化包级别的变量 72. 导入子模块,简化使用 83. 定义__all__,控制from package import *的行为 9""" 10 11# 包级别的变量 12VERSION = "1.0.0" 13 14# 简化导入:用户可以直接from mymath import add 15from .basic import add, subtract 16from .advanced import power, sqrt 17 18# 定义公开接口 19__all__ = ['add', 'subtract', 'power', 'sqrt', 'VERSION'] 20 21# 包初始化代码 22print(f"mymath包已加载,版本:{VERSION}") 使用包: 1# 方式1:直接从包导入(因为__init__.py中重新导出了) 2from mymath import add, power 3print(add(1, 2)) # 3 4print(power(2, 3)) # 8 5 6# 方式2:从子模块导入 7from mymath.basic import add 8from mymath.advanced import power 9 10# 方式3:导入整个包 11import mymath 12print(mymath.add(1, 2)) 13print(mymath.VERSION) 3. 子包和嵌套结构 包可以包含子包,形成层次结构: ...

2024-09-20 · 4 min · 701 words · 老墨