Appearance
继承与魔术方法
类可以继承另一个类——子类获得父类的属性和方法,还能加自己的东西或改写父类的行为。继承表达的是"是某种"关系:PublishedArticle 是 Article 的一种,AdminUser 是 User 的一种。
魔术方法是 Python 预定义的特殊方法,名字前后有双下划线——__init__ __str__ __eq__ 等。它们让对象支持内置操作:print(obj) 调用 __str__,obj1 == obj2 调用 __eq__。
一、继承的基本写法
python
class Article:
def __init__(self, title, author):
self.title = title
self.author = author
self.status = "草稿"
def publish(self):
self.status = "已发布"
print(f"{self.title} 已发布")
class PublishedArticle(Article):
"""已发布文章——创建时就直接是已发布状态。"""
def __init__(self, title, author, publish_date):
super().__init__(title, author) # 调用父类的 __init__
self.status = "已发布" # 覆盖默认值
self.publish_date = publish_date # 子类自己的属性
def show_info(self):
print(f"《{self.title}》by {self.author}")
print(f"状态:{self.status},发布日期:{self.publish_date}")PublishedArticle(Article) 表示 PublishedArticle 继承 Article。super().__init__(title, author) 调用父类的初始化方法,子类再加自己的逻辑。
python
article = PublishedArticle("Python 入门", "张三", "2024-06-01")
article.show_info()
# 《Python 入门》by 张三
# 状态:已发布,发布日期:2024-06-01
article.publish() # 继承自父类的方法仍然可用
# Python 入门 已发布子类能用父类的方法(publish),也能加自己的方法(show_info)。
二、重写父类方法
子类可以重新定义父类的方法,覆盖原有行为:
python
class Article:
def __init__(self, title, author):
self.title = title
self.author = author
self.status = "草稿"
def get_summary(self):
return f"{self.title}({self.author})"
class FeaturedArticle(Article):
"""精选文章——摘要里要带"推荐"标记。"""
def get_summary(self):
# 重写父类方法
return f"[推荐] {self.title}({self.author})"
a1 = Article("Python 入门", "张三")
a2 = FeaturedArticle("Go 并发", "李四")
print(a1.get_summary()) # Python 入门(张三)
print(a2.get_summary()) # [推荐] Go 并发(李四)FeaturedArticle 重写了 get_summary,调用时走子类的版本。
什么时候用继承? 当子类确实是父类的一种特殊情况时。FeaturedArticle 是 Article 的一种,用继承合理;Article 和 Author 是"文章有作者"的关系,用组合更合适。继承容易被滥用——能用组合就优先用组合。
三、魔术方法
3.1 __str__——print 时显示什么
python
class Contact:
def __init__(self, name, phone):
self.name = name
self.phone = phone
def __str__(self):
return f"{self.name}({self.phone})"
c = Contact("张三", "13800138000")
print(c) # 张三(13800138000)不写 __str__ 时,print(c) 输出的是类似 <__main__.Contact object at 0x...> 这种内存地址——对排查没帮助。写了 __str__,print 时输出人类可读的内容。
3.2 __repr__——调试时显示什么
__repr__ 是给开发者看的,通常写成"能还原对象的代码":
python
class Contact:
def __init__(self, name, phone):
self.name = name
self.phone = phone
def __repr__(self):
return f"Contact({self.name!r}, {self.phone!r})"
c = Contact("张三", "13800138000")
print(c) # Contact('张三', '13800138000')在交互式环境或调试器里直接敲变量名,显示的是 __repr__ 的输出。如果只定义一个,优先定义 __repr__——没有 __str__ 时,print 也会用 __repr__。
3.3 __eq__——两个对象相等怎么判断
python
class Contact:
def __init__(self, name, phone):
self.name = name
self.phone = phone
def __eq__(self, other):
# 两个对象的 name 和 phone 都相等才认为相等
return self.name == other.name and self.phone == other.phone
c1 = Contact("张三", "13800138000")
c2 = Contact("张三", "13800138000")
c3 = Contact("张三", "13900139000")
print(c1 == c2) # True
print(c1 == c3) # False不写 __eq__ 时,c1 == c2 比较的是内存地址(两个不同对象,False)。写了 __eq__,可以按业务逻辑定义"相等"。
3.4 __len__——len() 返回什么
python
class Article:
def __init__(self, title, content):
self.title = title
self.content = content
def __len__(self):
return len(self.content)
article = Article("Python 入门", "Python 是一门很好学的编程语言。")
print(len(article)) # 14len(article) 调用 article.__len__()。
3.5 __bool__——bool() 返回什么
python
class Article:
def __init__(self, title, content=""):
self.title = title
self.content = content
def __bool__(self):
# 内容不为空才认为是"真"
return len(self.content) > 0
a1 = Article("空文章")
a2 = Article("正常文章", "有一些内容")
print(bool(a1)) # False
print(bool(a2)) # True
if a1:
print("有内容")
else:
print("内容为空") # 走这里不写 __bool__ 时,对象默认是 True。写了可以定义"什么情况下这个对象算真"。
四、常用魔术方法一览
| 方法 | 触发场景 | 返回 |
|---|---|---|
__init__ | 创建对象时 Article(...) | 无 |
__str__ | print(obj) str(obj) | 字符串 |
__repr__ | 调试时直接显示 | 字符串 |
__eq__ | obj1 == obj2 | 布尔值 |
__lt__ | obj1 < obj2 | 布尔值 |
__len__ | len(obj) | 整数 |
__bool__ | bool(obj) if obj | 布尔值 |
__getitem__ | obj[key] | 取到的值 |
__setitem__ | obj[key] = value | 无 |
__contains__ | key in obj | 布尔值 |
__iter__ | for item in obj | 迭代器 |
这些方法让自定义对象表现得像内置类型——能 print、能比大小、能迭代、能判断包含关系。不用全记,用到再查。最常用的是 __init__ __str__ __repr__ __eq__ 这四个,其他用到场景再补。
五、继承与组合
继承表达"是某种",组合表达"有某个"。继承容易被过度使用——新手很容易把"文章有作者"写成"文章继承作者",这是错的。
判断标准:问自己"子类是父类的一种吗?"。FeaturedArticle 是 Article 的一种,用继承对;Article 不是 Author 的一种,用组合对。