#Python 类(Class)知识点总结
📺 推荐参考视频(实在搞不懂时去看):
- ROS 实战部分:B站搜索 “鱼香ROS机器人”,该 UP 主的 ROS2 系列课程讲解细致,本文档是看这个视频的时候记录的
- Python 类的基础语法:B站 林粒粒呀 的《三小时入门 Python》对"类"这个概念讲得通俗易懂,适合零基础快速理解面向对象编程
本文档作为上述视频的文字配套笔记,建议先看视频,本文档查阅具体语法细节作为辅助。
目录
- 什么是类?为什么需要类?
- 定义类的基本语法
__init__— 构造函数self— 对象自身- 实例变量 vs 类变量
- 实例方法
- 封装
- 继承
super().__init__()— 调用父类初始化- 实例化
- 什么时候需要
__init__?什么时候不需要? - 在 ROS2 中的固定套路
- 与导入模块的关系
- 术语速查表
1. 什么是类?为什么需要类?
类是一种"组织代码的方式"
把数据(属性)和操作数据的方法(函数)打包在一起,形成一个整体。这个整体就叫类。
为什么要用类?
如果不使用类,数据和行为是分散的:
# 不用类:数据归数据,函数归函数
name = "法外狂徒张三"
age = 18
def eat(person_name, person_age, food):
print(f"我叫{person_name},今年{person_age}岁,吃{food}")
eat(name, age, "鱼香肉丝")
每次调用 eat 都要把 name 和 age 重新传一遍。数据和操作数据的行为是分离的。
使用类之后:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self, food):
print(f"我叫{self.name},今年{self.age}岁,吃{food}")
p = Person("法外狂徒张三", 18)
p.eat("鱼香肉丝") # 不需要再传 name 和 age,对象自己记得
数据(name、age)和行为(eat)绑定在一起。调用 eat 时对象知道自己的名字和年龄。
2. 定义类的基本语法
class 类名:
# 类体(属性、方法)
示例:
class Download:
def download(self, url, callback):
...
| 部分 | 含义 | 规则 |
|---|---|---|
class | Python 关键字 | 必须写 |
类名 | 你自己取的名字 | 使用大驼峰命名法(每个单词首字母大写),如 Download、PersonNode |
: | 冒号 | 表示类体开始,下面必须缩进 |
类名命名约定
class PersonNode # ✅ 大驼峰:每个单词首字母大写
class person_node # ❌ 不推荐:小写加下划线是变量/函数的命名方式
class personNode # ❌ 不推荐:小驼峰不是 Python 类的规范
3. __init__ — 构造函数
是什么?
__init__ 是一个特殊方法(双下划线开头和结尾)。当你创建对象时,Python 会自动调用它。
作用?
在对象诞生时做初始化工作——把初始数据存到对象上。
语法
def __init__(self, 参数1, 参数2, ...):
self.某个属性 = 参数1
self.另一个属性 = 参数2
示例
class Person:
def __init__(self, name, age):
self.name = name # 把传来的 name 存为这个对象的 name 属性
self.age = age # 把传来的 age 存为这个对象的 age 属性
当你写 p = Person("张三", 18) 时,Python 内部流程:
1. 创建一个空的 Person 对象
2. 自动调用 Person.__init__(这个空对象, "张三", 18)
3. __init__ 里:
- self.name = "张三" → 对象有了 name 属性
- self.age = 18 → 对象有了 age 属性
4. 返回这个对象,赋给变量 p
什么时候写?什么时候不写?
| 情况 | 需要 __init__? |
|---|---|
创建对象时需要存储数据(后面方法中需要用到 self.xxx) | ✅ 需要 |
| 每次干活需要的数据都从参数直接传入,不需要提前存储 | ❌ 不需要 |
不写 __init__ 的例子:
class Download:
def download(self, url, callback):
# url 和 callback 每次调用时直接从参数传入,不需要存
...
4. self — 对象自身
self 是什么?
self 代表调用这个方法的那个对象本身。
为什么每个方法都要写 self?
Python 规定:实例方法的第一个参数必须是 self(虽然你可以给它取别的名字,但全世界都用 self)。
调用时不需要手动传 self
class Dog:
def bark(self):
print(f"{self.name} 在叫") # self 就是调用时的那个对象
dog1 = Dog()
dog1.bark() # 你写了 dog1.bark(),Python 自动把 dog1 传给 self
self 的用途:访问对象的属性和方法
class Person:
def __init__(self, name):
self.name = name # 把 name 存为 self 的属性
def greet(self):
print(f"我是{self.name}") # 通过 self 访问这个对象的 name 属性
5. 实例变量 vs 类变量
实例变量
属于每个对象自己,每个对象的值可以不同。在 __init__ 里用 self.xxx 定义。
class Dog:
def __init__(self, name):
self.name = name # 实例变量:每只狗的名字不同
dog1 = Dog("旺财")
dog2 = Dog("来福")
print(dog1.name) # 旺财
print(dog2.name) # 来福
类变量
属于整个类,所有对象共享同一份数据。在类体里、方法外面定义。
class Dog:
species = "犬科" # 类变量:所有狗都是犬科
def __init__(self, name):
self.name = name # 实例变量
dog1 = Dog("旺财")
dog2 = Dog("来福")
print(dog1.species) # 犬科
print(dog2.species) # 犬科
| 实例变量 | 类变量 | |
|---|---|---|
| 属于谁 | 每个对象自己 | 整个类,所有对象共享 |
| 定义位置 | __init__ 里,用 self.xxx | 类体里、方法外 |
| 访问方式 | self.xxx | 类名.xxx 或 self.xxx |
| 每个对象值相同吗? | ❌ 可以不同 | ✅ 所有对象一样 |
6. 实例方法
定义在类里面的函数叫方法。第一个参数必须是 self。
class Person:
def eat(self, food_name): # self + 其他参数
print(f"吃{food_name}")
def sleep(self, hours): # 可以有多个自定义方法
print(f"睡{hours}小时")
调用方式:
p = Person()
p.eat("鱼香肉丝") # 对象.方法名(参数),不需要传 self
7. 封装
封装的意思是:把数据和操作数据的方法捆绑在一起,隐藏内部细节,对外只暴露简单的接口。
class Download:
def download(self, url, callback):
# 内部:怎么发 HTTP 请求、怎么处理编码...
response = requests.get(url)
response.encoding = 'utf-8'
callback(url, response.text)
def start_download(self, url, callback):
# 内部:怎么开线程...
thread = threading.Thread(target=self.download, args=(url, callback))
thread.start()
外部使用时:
d = Download()
d.start_download('http://...', my_callback)
使用者只需要知道"调用 start_download 并传网址和回调函数",不需要知道内部是怎么开线程、怎么发 HTTP 请求的。
8. 继承
语法
class 子类名(父类名):
...
含义
子类自动获得父类的所有属性和方法,不用重新写一遍。子类还可以添加自己的新功能。
代码示例
# 父类
class PersonNode(Node):
def __init__(self, node_name, name, age):
super().__init__(node_name)
self.name = name
self.age = age
def eat(self, food_name):
self.get_logger().info(f'我叫{self.name},吃{food_name}')
# 子类:继承 PersonNode
class WriterNode(PersonNode):
def __init__(self, name, age, book):
super().__init__(name, age) # 调用父类的 __init__
self.book = book # 新增自己的属性
继承链和能力叠加
Node ← ROS2 提供:get_logger()、create_publisher() 等
└── PersonNode ← 加了:name、age、eat()
└── WriterNode ← 加了:book
WriterNode 的对象同时拥有这三层的全部能力。
为什么用继承?
- 不用重复造轮子:
WriterNode不需要重写eat(),直接继承就能用 - 层次清晰:每一层只管自己的新增部分
- 容易扩展:需要新类型时再继承一层即可
9. super().__init__() — 调用父类初始化
含义
super() 返回当前类的父类。super().__init__(...) 就是调用父类的 __init__,让父类先完成它的初始化工作。
为什么要调用它?
父类可能有自己的初始化逻辑(比如把节点名注册到 ROS2 系统)。如果不调用,父类的初始化就没执行,后续功能可能失效。
在 ROS2 节点中必须写
class PersonNode(Node):
def __init__(self, node_name, name, age):
super().__init__(node_name) # 必须!把节点名登记到 ROS2 系统
...
如果删掉这行,ROS2 不知道这个节点存在,get_logger()、发布、订阅统统失效。
多层继承中的 super()
Node.__init__ ← 最底层:向 ROS2 登记
↑ 被调用
PersonNode.__init__ ← 中间层:存 name、age
↑ 被调用
WriterNode.__init__ ← 最上层:存 book
每一层的 super().__init__() 往上调用,最终整条链都执行。
10. 实例化
语法
对象变量 = 类名(参数1, 参数2, ...)
含义
用"类"这个模板,造出一个具体的"对象"。
示例
# 不需要 __init__ 的类
d = Download() # 空括号
# 需要 __init__ 的类
node = PersonNode('person_node', '法外狂徒张三', 18) # 传参数
实例化时 Python 做了什么?
node = PersonNode('person_node', '法外狂徒张三', 18)
内部流程:
1. 分配内存 → 创建一个空的 PersonNode 对象
2. 自动调用 PersonNode.__init__(空对象, 'person_node', '法外狂徒张三', 18)
3. __init__ 内部执行:
→ super().__init__('person_node') 向 ROS2 登记
→ self.age = 18 存年龄
→ self.name = '法外狂徒张三' 存名字
4. 返回创建好的对象,赋给变量 node
11. 什么时候需要 __init__?什么时候不需要?
需要 __init__
对象有自己的"记忆"——方法里要用到 self.xxx:
class PersonNode(Node):
def __init__(self, node_name, name, age):
super().__init__(node_name)
self.age = age # 存起来
self.name = name # 存起来
def eat(self, food_name):
# 这里用到 self.name 和 self.age,所以必须先在 __init__ 里存
self.get_logger().info(f'我叫{self.name},今年{self.age}岁,吃{food_name}')
不需要 __init__
每次方法调用需要的所有东西都从参数直接传入,不需要存:
class Download:
def download(self, url, callback):
# url 和 callback 从参数直接来,不需要 self.url
...
def start_download(self, url, callback):
# 同理
...
12. 在 ROS2 中的固定套路
以后你写任何 ROS2 Python 节点,都遵循这个骨架:
import rclpy
from rclpy.node import Node
class 你的节点名(Node): # ① 继承 Node
def __init__(self, ...): # ② 构造函数
super().__init__('节点名') # ③ 必须!向 ROS2 登记
self.xxx = ... # ④ 你自己的属性
# ⑤ 在这里创建发布者、订阅者、定时器...
def 你的方法(self, ...): # ⑥ 自定义方法
...
def main(args=None):
rclpy.init(args=args)
node = 你的节点名(...) # ⑦ 实例化
rclpy.spin(node)
rclpy.shutdown()
if __name__ == '__main__':
main()
13. 与导入模块的关系
从系统库导入
from rclpy.node import Node
翻译:去 rclpy 库的 node.py 文件里,把 Node 类拿出来给我用。
从你自己写的文件导入
from demo_python_pkg.person_node import PersonNode
翻译:去 demo_python_pkg 包的 person_node.py 文件里,把 PersonNode 类拿出来。
这样 WriterNode 就可以继承 PersonNode,不用把代码重写一遍。
14. 术语速查表
| 术语 | 含义 | 示例 |
|---|---|---|
| 类(Class) | 一个模板,定义对象的属性和行为 | class PersonNode: |
| 对象(Object) | 用类造出来的具体实例 | node = PersonNode(...) |
| 实例化 | 用类创建对象的过程 | d = Download() |
__init__ | 构造函数,创建对象时自动调用 | def __init__(self, ...): |
self | 代表对象自身,方法第一个参数 | self.name = name |
| 实例变量 | 属于每个对象自己的数据 | self.age |
| 类变量 | 属于整个类、所有对象共享的数据 | species = "犬科" |
| 实例方法 | 定义在类里的函数,第一个参数是 self | def eat(self, food): |
| 封装 | 隐藏内部细节,对外暴露简单接口 | start_download 内部开线程,外部不用管 |
| 继承 | 子类自动获得父类的所有能力 | class WriterNode(PersonNode): |
super() | 指代父类 | super().__init__(...) |
| 父类 / 子类 | 被继承的类叫父类,继承的类叫子类 | Node 是父类,PersonNode 是子类 |
📝 核心口诀:类是把数据和行为打包的模板;继承让你不用重复造轮子;
super().__init__()是 ROS2 节点的必写项;self永远代表"那个对象自己"。实在搞不懂时,回到开头的视频链接,跟着林粒粒呀和鱼香ROS机器人再走一遍。
更多推荐

所有评论(0)