目录
前言
一、类和对象
1.类→Class
1.1概念
1.2创建
2.对象→Object
2.1概念
2.2创建
二、属性和方法
1. 实例属性
2. 实例方法
3. 类属性
4. 类方法
5. 静态方法
5.1 综合应用
6. 构造方法
7. 初始化方法
8. 魔术方法
8.1 常用方法
8.2 案例参考
总结
前言
这章讲的面向对象编程(Object-Oriented Programming,简称OOP)是一种通过组织对象来设计程序的编程方法。为什么需要类和对象?对比面向过程编程(算账用算盘 vs 用Excel表格),说明OOP更贴合现实世界思维(万物皆对象),代码更易组织、复用和扩展。OOP的核心思想可归纳为四大特性:封装、继承、多态和抽象。
类是对一类对象的抽象,是对象的模板或蓝图。它定义了对象的属性(特征)和方法(功能)。
“类”就像“汽车设计图”定义了所有汽车都应该有轮子、发动机、方向盘(属性),都应该能跑、能刹车、能鸣笛(方法)。它本身不是一辆车,但所有的汽车都依据它来制造。
类就是提供了一种结构化的方式来描述复杂的事物或概念。
例如:
1、数据成员:表明事物的特征。 相当于变量
2、方法成员:表明事物的功能。 相当于函数
3、通过 class 关键字定义类。
4、类的创建语句语法:
class 类名 (继承列表):
实例属性(类内的变量) 定义
实例方法(类内的函数method) 定义
类变量(class variable) 定义
类方法(@classmethod) 定义
静态方法(@staticmethod) 定义
创建一个Dog类
class Dog: # 关键字class + 类名(通常大写开头,遵循驼峰命名)
# 初始化方法(构造器)-- 后面详讲,这里先会用
def __init__(self, name, breed): # self代表未来创建的对象实例本身
# 定义属性:self.属性名
self.name = name # 实例属性:狗狗的名字
self.breed = breed # 实例属性:狗狗的品种
self.age = 0 # 实例属性:初始年龄为0
我们详细解释一下这个类:
class Dog: 声明一个名为Dog的类。
def __init__(self, ...): 一个特殊的方法(构造方法/初始化方法),当创建一个新的Dog对象时自动调用。
self: 这是最关键的,它代表正在被创建的具体的那个狗的对象(实例)。通过self.name = name,我们给这只具体的狗设置了一个名为name的属性,其值为传入的参数name。
核心: __init__方法是在初始化具体对象时使用的,依据类给具体对象(self)赋予具体的属性值。
注意点
人以群分,每个人就是一个对象。 对象是类的实例化,它是根据类的描述(蓝图)创建出来的一个实体,是类的实际数据存储,具有类所定义的属性和方法。
例如刚才类是“根据图纸造出来的车”。根据“汽车设计图”(类),我制造了一辆车牌是云A·88888的黑色奔驰(对象1),然后又制造了一辆车牌是云A·7777777白色宝马(对象2)。它们都是汽车(类),但都是具体的、拥有各自属性的个体(对象)。
构造函数调用表达式为:
变量 = 类名([参数])
1变量存储的是实例化后的对象地址
2 类参数按照初始化方法的形参传递
我们以刚才的Dog类接着举例
# 创建Dog类的对象 (实例化)
my_dog = Dog("旺财", "金毛") # 调用类名(参数) 相当于自动调用__init__
your_dog = Dog("小黑", "泰迪")
# 访问对象的属性
print(my_dog.name) # 输出: 旺财
print(your_dog.breed) # 输出: 泰迪
# 改变对象的属性
my_dog.age = 3 # 旺财3岁了
your_dog.age = 2 # 小黑2岁了
print(my_dog.age) # 输出: 3
我们详细解释一下这个代码:
my_dog = Dog("旺财", "金毛"):这是实例化的关键一步
解释器会:
1、在内存中开辟一块空间存放一个新的Dog对象。
2、自动调用Dog.__init__(self, "旺财", "金毛")。
这里的self指的就是这块新开辟的内存空间(新对象)!解释器把self指向新对象,然后把"旺财"传给name参数,"金毛"传给breed参数。
在__init__方法内部,语句self.name = "旺财"就是在给这个新对象(self)设置一个属性name,值是"旺财"。self.breed = "金毛"同理。
my_dog:是一个变量,它指向(引用)了内存中那个代表“旺财”的对象。操作my_dog就是在操作旺财这只具体的狗。
有了类和对象,它们得‘有血有肉’(属性)还得‘能动能做’(方法)。属性就像对象的内在特征(身高、体重、颜色),方法就是对象能做什么(跑、跳)。
1、每个实例有自己的变量,称为实例变量(也叫属性)
2、属性的使用语法
实例.属性名
class Dog:
def eat(self, food):
print(self.color, '的', self.kinds, '正在吃', food)
# 创建一个实例:
dog1 = Dog()
dog1.kinds = "京巴" # 添加属性
dog1.color = "白色"
dog1.color = "黄色" # 修改属性
print(dog1.color, '的', dog1.kinds)
dog2 = Dog()
dog2.kinds = "藏獒"
dog2.color = "棕色"
print(dog2.color, '的', dog2.kinds)
实例方法和实例属性(实例变量)结合在一起用:
class Dog:
def eat(self, food):
print(self.color, '的', self.kinds, '正在吃', food)
# 创建第一个对象
dog1 = Dog()
dog1.kinds = '京巴' # 添加属性kinds
dog1.color = '白色' # 添加属性color
dog1.eat("骨头")
dog2 = Dog()
dog2.kinds = '牧羊犬'
dog2.color = '灰色'
dog2.eat('包子')
class 类名(继承列表):
def 实例方法名(self, 参数1, 参数2, ...):
"文档字符串"
语句块
调用:
实例.实例方法名(调用传参)
# 或
类名.实例方法名(实例, 调用传参)
带有实例方法的简单的Dog类
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类的文档信息
类属性是类的属性,此属性属于类,不属于此类的实例
作用:通常用来存储该类创建的对象的共有属性
类属性说明:
类属性,可以通过该类直接访问
类属性,可以通过类的实例直接访问
类属性示例:
class Human:
total_count = 0 # 创建类属性 self.name = name
def __init__(self, name):
self.name = name
print(Human.total_count)
h1 = Human("小张")
print(h1.total_count)
类方法是用于描述类的行为的方法,类方法属于类,不属于该类创建的对象
说明:
类方法需要使用@classmethod装饰器定义
类方法至少有一个形参用于绑定类,约定为 $cls$
类和该类的实例都可以调用类方法
类方法不能访问此类创建的对象的实例属性
类方法示例1
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())
类方法示例2
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() # 事出反常必有妖
静态方法定义在类的内部,作用域是类内部
说明:
使用@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
小案例:
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
构造方法 __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)
一旦对象被创建,Python会调用 __init__() 来初始化对象的属性。
语法格式:
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__ 方法,初始化对象
"""
魔术方法是一种特殊的方法,用双下划线包裹,例如 __init__ , __str__ , __add__ 等。这些方法允许您自定义类的行为,以便与内置Python功能(如+运算符、迭代、字符串表示等)交互。
1. __init__(self, ...) : 初始化对象,通常用于设置对象的属性。
2. __str__(self) : 定义对象的字符串表示形式,可通过 str(object) 或 print(object) 调用。例如,您可以返回一个字符串,描述对象的属性。
3. __repr__(self) : 定义对象的“官方”字符串表示形式,通常用于调试。可通过`repr(object)`调用。
4. __len__(self) : 定义对象的长度,可通过`len(object)`调用。通常在自定义容器类中使用。
5. __getitem__(self, key) : 定义对象的索引操作,使对象可被像列表或字典一样索引。例如, object[key] 。
6. __setitem__(self, key, value) : 定义对象的赋值操作,使对象可像列表或字典一样赋值。例如, object[key] = value 。
7. __delitem__(self, key) : 定义对象的删除操作,使对象可像列表或字典一样删除元素。例如, del object[key] 。
8. __iter__(self) : 定义迭代器,使对象可迭代,可用于 for 循环。
9. __next__(self) : 定义迭代器的下一个元素,通常与 __iter__ 一起使用。
10. __add__(self, other) : 定义对象相加的行为,使对象可以使用 + 运算符相加。例如, object1 + object2 。
11. __sub__(self, other) : 定义对象相减的行为,使对象可以使用 - 运算符相减。
12. __eq__(self, other) : 定义对象相等性的行为,使对象可以使用 == 运算符比较。
13. __lt__(self, other) : 定义对象小于其他对象的行为,使对象可以使用 < 运算符比较。
14. __gt__(self, other) : 定义对象大于其他对象的行为,使对象可以使用 > 运算符比较。
15. __call__(self, other) 是一个特殊的方法(也称为“魔法方法”),它允许一个对象像函数一样被调用。
1、__str__() :定义 print() 或 str() 函数调用时的输出字符串表示。
__str__() 方法用来定义当你调用 print() 或 str() 时对象应该返回的字符串。
该方法必须返回一个字符串,通常用于给用户可读的对象描述。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Person: {self.name}, Age: {self.age}"
p = Person("Alice", 30)
print(p) # 输出: Person: Alice, Age: 30
作用:
使得你的对象在打印时更加友好和可读。
提供更好的调试输出。
2、 __repr__() :定义 repr() 函数的输出,通常用于开发和调试,给出对象的正式字符串表示。
__repr__() 方法用于返回一个可以用来重新创建对象的字符串,理想情况下,返回的字符串应当是合法的 Python 表达式。
如果你没有定义 __str__() ,那么 __repr__() 会被用来替代 str() 和 print() 。
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("Alice", 30)
print(repr(p)) # 输出: Person('Alice', 30)
作用:
__repr__() 是供开发人员使用的,输出的字符串应该尽可能接近于可以创建该对象的代码。
在调试时使用 repr() 更加方便,它提供了一个清晰的对象表示。
因为就不一一来举例了,我们用一个小小的综合案例:
题目: 创建一个简单的Fraction(分数)类。
要求支持:
1、创建 (__init__)
2、字符串表示 (__str__)
3、精确表示 (__repr__)
4、相等比较 (__eq__)
5、加法 (__add__)
6、约分(辅助方法)
class Fraction:
#一个简单的分数类
def __init__(self, numerator, denominator=1):
#初始化分子和分母。分母不能为0。自动约分。
if denominator == 0:
raise ValueError("分母不能为0!")
self.numer = numerator # 分子
self.denom = denominator # 分母
self._simplify() # 在创建时就进行约分
def _gcd(self, a, b):
#计算最大公约数 (gcd) 的辅助函数
while b:
a, b = b, a % b
return a
def _simplify(self):
#约分私有方法
g = self._gcd(abs(self.numer), abs(self.denom))
self.numer //= g
self.denom //= g
# 处理符号,保证分母为正 (分子带符号)
if self.denom < 0:
self.numer = -self.numer
self.denom = -self.denom
def __str__(self):
#友好的字符串表示:3/4
return f"{self.numer}/{self.denom}"
def __repr__(self):
#无歧义表示:Fraction(3,4)
return f"Fraction({self.numer}, {self.denom})"
def __eq__(self, other):
#定义相等:约分后的分子分母分别相等即为相等
if not isinstance(other, Fraction):
return False # 也可以尝试类型转换
return self.numer == other.numer and self.denom == other.denom
def __add__(self, other):
#分数加法: (a/b) + (c/d) = (ad+bc)/(bd)
if not isinstance(other, Fraction):
raise TypeError("只能与另一个Fraction相加")
new_numer = self.numer * other.denom + other.numer * self.denom
new_denom = self.denom * other.denom
return Fraction(new_numer, new_denom) # 返回新的约分后的分数对象
# 使用示例
f1 = Fraction(3, 4)
f2 = Fraction(1, 8)
print(f1) # 输出: 3/4
print(repr(f1)) # 输出: Fraction(3, 4) (或接近形式)
print(f1 + f2) # 输出: 7/8 (会自动约分)
f3 = Fraction(1, 2)
f4 = Fraction(2, 4)
print(f3 == f4) # 输出: True (约分后都是1/2)
这个例子展示了如何利用魔术方法让自定义的Fraction
类拥有与Python内置类型类似的行为和接口:自然创建、易读打印、精确描述、自定义相等性判断以及直观的加法运算符。
这一章讲解了面向对象编程,讲述了oop的核心:类与对象,类是对象的蓝图,定义属性和其方法;对象是类的具体实例。属性分为实例属性和类属性;方法包括实例方法、类方法和静态方法。魔术方法自定义对象行为,实现代码复用和扩展。这一章可能有点绕,但只要了解代码内部的逻辑,大多都是简单的。