Python 类和对象:从 "图纸" 到 "实物" 的编程思维
面向对象编程(Object-Oriented Programming,简称OOP )是一种通过组织对象来编程的方法。
1.初识类和对象:用生活例子看透核心概念
1.1类-class
物与类聚
1.1.1概念
类是对一类对象的抽象,是对象的模板或蓝图。它定义了对象的属性(特征)和方法(功能)。例如 ‘人’类。
1.1.2创建
数据成员:表明事物的特征。 相当于变量
方法成员:表明事物的功能。 相当于函数
通过
class
关键字定义类。类的创建语句语法:
'''
class 类名 (继承列表):
实例属性(类内的变量) 定义
实例方法(类内的函数method) 定义
类变量(class variable) 定义
类方法(@classmethod) 定义
静态方法(@staticmethod) 定义
'''
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
print(f"{self.name} says Woof!")
类名就是标识符,建议首字母大写
类名实质上就是变量,它绑定一个类
self代表类实例化的对象本身
1.2对象-object
人以群分,每个人就是一个对象。
1.2.1概念
对象是类的 "实例化"—— 用类创建出的具体个体。每个对象都有自己的属性值,但共享类的方法。
1.2.2创建
构造函数调用表达式
变量 = 类名([参数])
说明:
变量存储的是实例化后的对象地址
类参数按照初始化方法的形参传递
对象是类的实例,具有类定义的属性和方法。
通过调用类的构造函数来创建对象。
每个对象有自己的状态,但共享方法。
示例:
class Dog:
pass
# 创建第一个实例:
dog1 = Dog()
print(id(dog1)) # 打印这个对象的ID
# 创建第二个实例对象
dog2 = Dog() # dog2 绑定一个Dog类型的对象
print(id(dog2))
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
print(f"My name is {self.name} and I am {self.age} years old.")
person1 = Person("Alice", 25)
person2 = Person("Bob", 30)
一句话总结:
- 类是 "模板",定义共性;对象是 "实例",有自己的个性。
- 就像 "汽车类" 是模板,你家的 "特斯拉" 和邻居的 "比亚迪" 是对象。
2.属性和方法
类的属性和方法是类的核心组成部分,它们用于定义类的状态和行为。
2.1属性:对象的 "特征" 分类
属性是对象的特征(比如名字、年龄、颜色),按归属可分为实例属性和类属性。
2.1.1实例属性:每个对象独有的特征
实例属性是每个对象自己的 "个性特征"—— 不同对象可以有不同的值。
定义方式:在初始化方法__init__
中通过self.属性名
定义。
class Dog:
# 初始化方法:创建对象时自动调用,给对象设置初始属性
def __init__(self, name, age): # self代表当前对象
self.name = name # 实例属性:名字(每个狗不同)
self.age = age # 实例属性:年龄(每个狗不同)
# 创建对象时传入属性值
dog1 = Dog("旺财", 3) # 旺财3岁
dog2 = Dog("小白", 2) # 小白2岁
# 访问实例属性:对象名.属性名
print(dog1.name) # 输出:旺财
print(dog2.age) # 输出:2
使用场景:描述对象的个性化特征(如人的姓名、手机的价格)。
2.1.2类属性:所有对象共享的特征
类属性是类本身的属性,所有对象共享同一个值 —— 改一次,所有对象都受影响。
定义方式:直接在类中定义(不在__init__
里)。
class Dog:
# 类属性:所有狗共享的特征(比如都属于"犬科")
species = "犬科" # 类属性:定义在类中,不在__init__里
def __init__(self, name):
self.name = name # 实例属性
# 创建对象
dog1 = Dog("旺财")
dog2 = Dog("小白")
# 访问类属性:类名.属性名 或 对象名.属性名
print(Dog.species) # 输出:犬科(推荐用类名访问)
print(dog1.species) # 输出:犬科(对象也能访问)
# 修改类属性:所有对象都会受影响
Dog.species = "哺乳纲犬科"
print(dog2.species) # 输出:哺乳纲犬科
使用场景:存储所有对象的共性数据(如学生类的 "学校名称"、汽车类的 "燃料类型")。
2.2方法:对象的 "功能" 分类
方法是对象的功能(比如说话、计算、移动),按作用范围可分为实例方法、类方法和静态方法。
2.2.1实例方法:操作对象自身的功能
实例方法是对象自己的功能,必须依赖具体对象才能调用,能访问实例属性和类属性。
- 定义方式:
第一个参数必须是self
(代表当前对象)。
- 调用
实例.实例方法名(调用传参)
或
类名.实例方法名(实例, 调用传参)
class 类名(继承列表):
def 实例方法名(self, 参数1, 参数2, ...):
"文档字符串"
语句块
示例1:
class Dog:
def __init__(self, name):
self.name = name # 实例属性
# 实例方法:狗叫(依赖具体对象的名字)
def bark(self):
print(f"{self.name}:汪汪!") # 访问实例属性self.name
# 实例方法:吃食物(带参数)
def eat(self, food):
print(f"{self.name}在吃{food}")
# 创建对象
dog = Dog("旺财")
# 调用实例方法:对象名.方法名()
dog.bark() # 输出:旺财:汪汪!
dog.eat("骨头") # 输出:旺财在吃骨头
使用场景:需要操作对象自身属性的功能(如人的 "自我介绍"、手机的 "拨打电话")。
示例2:
class Dog:
"""这是一个种小动物的定义
这种动物是狗(犬)类,用于创建各种各样的小狗
"""
def eat(self, food):
"""此方法用来描述小狗吃东西的行为"""
print("小狗正在吃", food)
def sleep(self, hour):
print("小狗睡了", hour, "小时!")
def play(self, obj):
print("小狗正在玩", obj)
dog1 = Dog()
dog1.eat("骨头")
dog1.sleep(1)
dog1.play('球')
help(Dog) # 可以看到Dog类的文档信息
2.2.2 类方法:操作类属性的功能
类方法属于类本身,不依赖具体对象,主要用于操作类属性。
说明
类方法需要使用@classmethod装饰器定义
类方法至少有一个形参用于绑定类,约定为 $cls$
类和该类的实例都可以调用类方法
类方法不能访问此类创建的对象的实例属
示例1
class School:
# 类属性:学生总数
total_students = 0
# 类方法:增加学生数量(操作类属性)
@classmethod
def add_student(cls): # cls代表School类
cls.total_students += 1 # 修改类属性
# 类方法:获取学生总数
@classmethod
def get_total(cls):
return cls.total_students
# 调用类方法:类名.方法名()(不需要创建对象)
School.add_student() # 增加1个学生
School.add_student() # 再增加1个
print(School.get_total()) # 输出:2
示例2
class A:
v = 0
@classmethod
def set_v(cls, value):
cls.v = value
@classmethod
def get_v(cls):
return cls.v
print(A.get_v())
A.set_v(100)
print(A.get_v())
a = A()
print(a.get_v())
- 示例3
class MyClass:
class_attr = 0 # 类属性
def __init__(self, value):
self.instance_attr = value # 实例属性
@classmethod
def modify_class_attr(cls, new_value):
cls.class_attr = new_value
print(f"类属性已修改为: {cls.class_attr}")
@classmethod
def try_modify_instance_attr(cls):
try:
cls.instance_attr = 10 # 事出反常必有妖
except AttributeError as e:
print(f"错误: {e}")
# 创建类的实例
obj = MyClass(5)
# 调用类方法修改类属性
MyClass.modify_class_attr(20) # 输出: 类属性已修改为: 20
MyClass.try_modify_instance_attr() # 事出反常必有妖
使用场景:需要修改或查询类属性的功能(如统计类的实例数量、批量修改共性参数)。
2.2.3 静态方法:类中的 "工具函数"
静态方法定义在类的内部,作用域是类内部
说明
使用@staticmethod装饰器定义
不需要self和cls参数
通过类或类实例调用
可以访问类属性,不能访问实例属性
使用场景:
逻辑上属于类的方法,但不依赖类或实例
让代码更有组织性
避免污染全局命名空间
静态方法示例
class A:
class_attr = 42 # 类属性
def __init__(self, value):
self.instance_attr = value # 实例属性
@staticmethod
def myadd(a, b):
# 只能访问传递的参数,不能访问实例属性
return a + b
# 创建类实例
a = A(10)
# 调用静态方法
print(A.myadd(100, 200)) # 输出: 300
print(a.myadd(300, 400)) # 输出: 700
示例2:
class Calculator:
# 静态方法:计算两个数的和(纯工具功能)
@staticmethod
def add(a, b):
return a + b
# 静态方法:计算两个数的积
@staticmethod
def multiply(a, b):
return a * b
# 调用静态方法:类名.方法名() 或 对象名.方法名()
print(Calculator.add(2, 3)) # 输出:5
calc = Calculator()
print(calc.multiply(4, 5)) # 输出:20
2.5.1 综合应用
小案例:
class BankAccount:
interest_rate = 0.05 # 类属性
def __init__(self, balance):
self.balance = balance
def apply_interest(self): # 实例方法
self.balance *= (1 + self.interest_rate)
@classmethod
def set_interest_rate(cls, rate): # 类方法
cls.interest_rate = rate
@staticmethod
def validate_amount(amount): # 静态方法
return amount > 0 # 只是个工具方法,不依赖实例/类
# 使用类方法修改利率
BankAccount.set_interest_rate(0.07)
# 创建账户
acc = BankAccount(1000)
acc.apply_interest()
print(acc.balance) # 1070.0
# 使用静态方法验证金额
print(BankAccount.validate_amount(-500)) # False
使用场景:逻辑上属于类,但不依赖类或对象的工具功能(如数学计算、数据验证)。
3.构造与初始化:对象的 "出生过程"
创建对象时,Python 会自动执行两个特殊方法:__new__
(造对象)和__init__
(初始化属性)。
3.1 __new__
:创建对象的 "产房"
构造方法__new__()
:
负责对象的 创建 和内存分配的。
在对象实例化时被调用,负责返回一个新的对象实例。
通常不需要显式地定义
__new__()
方法,Python会调用基类 $object$ 的__new__()
方法。
class MyClass:
def __new__(cls, *args, **kwargs):
print("调用 __new__ 方法,创建对象")
return super().__new__(cls)
def __init__(self, value):
print("调用 __init__ 方法,初始化对象")
self.value = value
# 创建对象
obj = MyClass(10)
3.2初始化方法__init__
:给对象 "装零件"
一旦对象被创建,Python会调用
__init__()
来初始化对象的属性。- 必须有
self
参数,代表刚创建的对象。 - 可以定义其他参数,创建对象时传入。
语法格式:
class 类名(继承列表):
def __init__(self[, 形参列表]):
语句块
# [] 代表其中的内容可省略
接收实参到
__init__
方法中代码示例:
class MyClass:
def __new__(cls, *args, **kwargs):
print("调用 __new__ 方法,创建对象")
return super().__new__(cls)
def __init__(self, value):
print("调用 __init__ 方法,初始化对象")
self.value = value
# 创建对象
obj = MyClass(10)
'''
调用 __new__ 方法,创建对象
调用 __init__ 方法,初始化对象
'''
4.魔术方法:让对象 "懂规矩" 的特殊技能
魔术方法是一种特殊的方法,用双下划线包裹,例如__init__
,__str__
,__add__
等。这些方法允许您自定义类的行为,以便与内置Python功能(如+运算符、迭代、字符串表示等)交互。
4.1 __str__
:自定义对象的 "自我介绍"
当用print()
或str()
输出对象时,会自动调用__str__
,返回一个用户友好的字符串。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 自定义打印格式
def __str__(self):
return f"我叫{self.name},今年{self.age}岁"
p = Person("小明", 18)
print(p) # 输出:我叫小明,今年18岁(自动调用__str__)
print(str(p)) # 输出:我叫小明,今年18岁(同样调用__str__)
4.2 __repr__
:给开发者看的 "官方说明"
当用repr()
查看对象时,会调用__repr__
,返回一个能重建对象的字符串(通常是代码形式)。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# 开发者友好的格式(可直接复制创建对象)
def __repr__(self):
return f"Person('{self.name}', {self.age})"
p = Person("小红", 20)
print(repr(p)) # 输出:Person('小红', 20)(适合调试)
小技巧:如果没定义__str__
,print()
会用__repr__
的结果。
4.3 __add__
:让对象支持 "+" 运算
自定义两个对象相加的逻辑,用+
时自动调用。
class Point:
def __init__(self, x, y):
self.x = x # x坐标
self.y = y # y坐标
# 自定义加法:两个点的x、y分别相加
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
# 配合打印,定义__repr__
def __repr__(self):
return f"Point({self.x}, {self.y})"
p1 = Point(1, 2)
p2 = Point(3, 4)
p3 = p1 + p2 # 自动调用__add__
print(p3) # 输出:Point(4, 6)
4.4 __sub__
:让对象支持 "-" 运算
类似__add__
,自定义减法逻辑。
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
# 自定义减法
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def __repr__(self):
return f"Point({self.x}, {self.y})"
p1 = Point(5, 5)
p2 = Point(2, 1)
print(p1 - p2) # 输出:Point(3, 4)
4.5 __len__
:让对象支持len()
函数
自定义len()
的返回值,通常用于表示对象的 "长度"。
class MyList:
def __init__(self, items):
self.items = items # 存储列表数据
# 自定义len()的结果为列表长度
def __len__(self):
return len(self.items)
my_list = MyList([1, 2, 3, 4])
print(len(my_list)) # 输出:4(自动调用__len__)
4.6 __getitem__
:让对象支持 "[]" 索引
自定义通过索引(如obj[0]
)访问对象的逻辑。
class MyList:
def __init__(self, items):
self.items = items
# 自定义索引访问:返回对应位置的元素
def __getitem__(self, index):
return self.items[index]
my_list = MyList([10, 20, 30])
print(my_list[1]) # 输出:20(自动调用__getitem__)
4.7 __setitem__
:让对象支持 "[]=" 赋值
自定义通过索引(如obj[0] = 5
)修改对象的逻辑。
class MyList:
def __init__(self, items):
self.items = items
def __setitem__(self, index, value):
self.items[index] = value # 修改对应位置的元素
my_list = MyList([10, 20, 30])
my_list[1] = 200 # 自动调用__setitem__
print(my_list.items) # 输出:[10, 200, 30]
4.8 __delitem__
:让对象支持 "del" 删除
自定义通过del obj[0]
删除元素的逻辑。
class MyList:
def __init__(self, items):
self.items = items
def __delitem__(self, index):
del self.items[index] # 删除对应位置的元素
my_list = MyList([10, 20, 30])
del my_list[0] # 自动调用__delitem__
print(my_list.items) # 输出:[20, 30]
4.9 __iter__
和__next__
:让对象支持 for 循环
实现这两个方法,对象就可以被 for 循环遍历(成为迭代器)。
class Counter:
def __init__(self, end):
self.end = end # 计数终点
self.current = 0 # 当前计数
# 迭代器必须返回自己
def __iter__(self):
return self
# 每次迭代返回下一个值
def __next__(self):
if self.current < self.end:
self.current += 1
return self.current
else:
# 停止迭代
raise StopIteration
# 用for循环遍历Counter对象
for num in Counter(3):
print(num) # 输出:1 → 2 → 3
4.10 __eq__
:自定义 "==" 比较逻辑
默认==
比较对象的内存地址,用__eq__
可自定义比较规则。
class Student:
def __init__(self, id, name):
self.id = id # 学号(唯一标识)
self.name = name
# 自定义相等:学号相同则认为相等
def __eq__(self, other):
return self.id == other.id
s1 = Student(1001, "小明")
s2 = Student(1001, "小红") # 学号相同
s3 = Student(1002, "小明") # 学号不同
print(s1 == s2) # 输出:True(学号相同)
print(s1 == s3) # 输出:False(学号不同)
4.11 __lt__
和__gt__
:自定义 "<" 和 ">" 比较
__lt__
定义小于逻辑,__gt__
定义大于逻辑。
class Product:
def __init__(self, price):
self.price = price
# 自定义小于:价格小则认为小
def __lt__(self, other):
return self.price < other.price
# 自定义大于:价格大则认为大
def __gt__(self, other):
return self.price > other.price
p1 = Product(100)
p2 = Product(200)
print(p1 < p2) # 输出:True(100 < 200)
print(p1 > p2) # 输出:False(100 > 200不成立)
4.12 __call__
:让对象像函数一样被调用
定义__call__
后,对象可以用对象名()
的形式调用。
class Adder:
def __init__(self, num):
self.num = num # 基础数字
# 让对象可调用:传入x,返回x + num
def __call__(self, x):
return x + self.num
add5 = Adder(5) # 创建一个"加5"的对象
print(add5(3)) # 输出:8(相当于调用函数,3+5)
print(add5(10)) # 输出:15(10+5)
5、实战练习:巩固类和对象的知识
设计一个 "图书" 类
- 实例属性:书名(name)、作者(author)、页数(pages)
- 类属性:类别(category = "书籍")
- 实例方法:
get_info()
打印图书信息 - 类方法:
set_category(new_cat)
修改类别 - 魔术方法:
__str__
自定义打印格式
class Book:
category = '书籍'
def __init__(self, name, autor, pages):
"""
初始化图书对象
参数:
name: 书名
author: 作者
pages: 页数
"""
self.name = name
self.autor = autor
self.pages = pages
def get_info(self):
"""返回格式化的图书信息字符串"""
return f'书名:{self.name}, 作者:{self.autor}, 页数{self.pages}'
@classmethod
def set_category(cls, new_cat):
"""修改图书类别"""
cls.category = new_cat
def __str__(self):
"""返回图书的简短描述"""
return f'{self.name} ({self.autor}著)'
book = Book("Python 编程", "张三", 300)
print(book) # 输出:Python 编程(张三 著)
book.get_info() # 输出:书名:Python 编程...
print(Book.category) # 输出:书籍
Book.set_category("技术图书")
print(book.category) # 输出:技术图书
总结:类和对象是编程的 "积木"
类和对象是面向对象编程的核心,它们让代码更贴近现实世界的逻辑:
- 用类定义 "模板",用对象创建 "实物";
- 用属性描述特征,用方法定义功能;
- 用魔术方法让对象支持 Python 的内置功能,更 "合群"。
学会类和对象后,你可以用更模块化、更易维护的方式编写复杂程序 —— 就像用积木搭房子,先做好基础零件(类),再组合出各种造型(程序)。