图表和截图搜不到:非文本内容怎么进检索链路
2026/10/14 9:52:43
欢迎来到第16天!
在之前的两天里,我们构建了 OOP 的大厦框架。今天,我们要进行内部装修,学习一些 Python 特有的"黑魔法"。
你是否好奇过:
print(obj)打印出来的是一串看不懂的地址,而别人的对象打印出来是清晰的信息?+号既能算数字加法,又能拼接字符串,甚至还能合并列表?self上吗?有没有属于类本身的属性?本节内容:
__add__,__eq__等)__init__中,使用self.xxx。每个对象独有一份,互不干扰。classTool:# 类属性:所有工具共享的计数器count=0def__init__(self,name):# 实例属性:每个工具的名字不同self.name=name# 每创建一个对象,类属性 count 加 1Tool.count+=1t1=Tool("锤子")t2=Tool("扳手")print(f"工具1:{t1.name}")# 锤子print(f"工具2:{t2.name}")# 扳手print(f"当前工具总数:{Tool.count}")# 2注意:访问类属性建议直接用
类名.属性名(如Tool.count)。虽然t1.count也能访问,但容易引起混淆。
Python 的类中可以定义三种方法,它们通过装饰器来区分。
self(指向对象)。@classmethod装饰。cls(指向类本身,而不是对象)。__init__创建对象) 或操作类属性。@staticmethod装饰。self和cls参数。classDateUtil:def__init__(self,year,month,day):self.year=year self.month=month self.day=day# 1. 实例方法:打印日期defshow(self):print(f"{self.year}-{self.month}-{self.day}")# 2. 类方法:通过字符串创建对象 (工厂模式)@classmethoddeffrom_string(cls,date_str):# date_str 格式 "2026-01-01"y,m,d=map(int,date_str.split("-"))returncls(y,m,d)# 相当于 DateUtil(y, m, d)# 3. 静态方法:验证日期是否有效 (不需要访问实例或类属性)@staticmethoddefis_valid(date_str):return"-"indate_str# 使用# 静态方法检查ifDateUtil.is_valid("2026-05-20"):# 类方法创建对象d=DateUtil.from_string("2026-05-20")# 实例方法显示d.show()# 2026-5-20在 Python 中,以双下划线开头和结尾的方法被称为魔术方法(也叫 Dunder Methods)。它们会在特定时机被自动调用。
__init__就是最常见的魔术方法。
当你print(obj)时,默认打印的是内存地址。实现__str__可以自定义打印内容。
classPerson:def__init__(self,name,age):self.name=name self.age=age# 面向用户:print() 时调用def__str__(self):returnf"Person(name='{self.name}', age={self.age})"# 面向开发者:命令行交互时调用 (通常用来排查问题)def__repr__(self):returnf"<Person{self.name}>"p=Person("Alice",25)print(p)# Person(name='Alice', age=25)为什么 Python 的字符串和列表可以相加?因为它们实现了__add__方法。我们也可以让自定义对象支持运算符。
示例:二维向量相加
classVector:def__init__(self,x,y):self.x=x self.y=ydef__str__(self):returnf"Vector({self.x},{self.y})"# 实现 + 号运算def__add__(self,other):# 返回一个新的 Vector 对象returnVector(self.x+other.x,self.y+other.y)# 实现 == 号比较def__eq__(self,other):returnself.x==other.xandself.y==other.y v1=Vector(2,4)v2=Vector(3,1)v3=v1+v2# 自动调用 v1.__add__(v2)print(v3)# Vector(5, 5)print(v1==v2)# Falseprint(v1==Vector(2,4))# True__len__(self): 响应len(obj)。__call__(self): 让对象能像函数一样被调用obj()。__getitem__(self, key): 让对象支持索引访问obj[key]。定义Cart类,内部用列表存储商品。
add(item): 添加商品。__len__: 返回商品数量。__str__: 打印所有商品名称。classCart:def__init__(self):self.items=[]defadd(self,item):self.items.append(item)def__len__(self):returnlen(self.items)def__str__(self):returnf"购物车包含:{', '.join(self.items)}"my_cart=Cart()my_cart.add("苹果")my_cart.add("牛奶")print(len(my_cart))# 2print(my_cart)# 购物车包含: 苹果, 牛奶实现一个简单的分数类,支持分数的相加。
例如:1/2 + 1/4 = 3/4。
classFraction:def__init__(self,top,bottom):self.top=top# 分子self.bottom=bottom# 分母def__str__(self):returnf"{self.top}/{self.bottom}"def__add__(self,other):# 通分公式: a/b + c/d = (ad + bc) / bdnew_top=self.top*other.bottom+other.top*self.bottom new_bottom=self.bottom*other.bottomreturnFraction(new_top,new_bottom)f1=Fraction(1,2)f2=Fraction(1,4)print(f1+f2)# 6/8 (暂时不涉及约分逻辑)到今天为止,OOP 的核心内容就讲完了。让我们回顾一下:
__init__和__new__有什么区别?__new__是真正创建对象实例的方法(构造器)。__init__是对象创建好后初始化属性的方法(初始化器)。__init__,除非你在做单例模式或继承不可变类型。@staticmethod?当你写了一个方法,发现它既不需要访问self(实例属性),也不需要访问cls(类属性),但逻辑上又跟这个类有关联时,就把它定义为静态方法。
关键要点:
print()的显示效果?写__str__。+运算?写__add__。self(对象) 和cls(类) 的使用场景。Person类,每实例化一个对象,类属性population加 1。实现一个__del__方法(析构函数,对象销毁时调用),让population减 1。MyTime类,包含hour,minute。实现__add__方法,支持两个时间相加(注意进位,如 1:30 + 1:40 = 3:10)。__new__方法实现一个单例类(无论创建多少次,都只返回同一个对象)。Day 17:异常处理 (Try-Except)- 程序报错了怎么办?直接崩溃吗?不!我们要学会优雅地处理错误,让程序坚不可摧。
系列导航: