Python训练营打卡 Day28
类的定义和方法
知识点回顾:
-
类的定义:菜谱的模板
类就像是餐厅的菜谱,它定义了如何制作一道菜的步骤和所需食材。
类是对属性和方法的封装,可以理解为模板,通过对模板实例化可以实现调用这个类的属性和方法。比如创建一个随机森林类,然后就可以调用他的训练和预测方法。现在我们来学一下自己定义一个类,这会让我们对于类这个对象理解的更加深刻
ps:类的操作很多,我们这里只说一些在深度学习领域最常见和通用的
class ClassicDish:# 类定义pass
一个常见的类的定义包括了:
-
关键字
class
:就像告诉厨师要开始定义一个菜谱。 -
类名
ClassicDish
:类似于菜谱的名字,比如“宫保鸡丁”,遵循大驼峰命名法。 -
冒号
:
:表示开始定义类的内容。 -
pass
:占位语句,表示类目前没有具体内容。 -
一个初始化函数__init__(self)
注意:注意:init左右各有两个下划线__,需要传入self这个特殊的参数。
pass占位语句:预留一个菜谱位置
假设你是一个餐厅的厨师,你正在编写一个新菜谱。你已经想好了要添加一个新的菜品,但目前还没有具体的烹饪步骤。为了确保这个菜品在菜单上有一个位置,你可以先创建一个“占位符”菜谱。
# 定义一个类,但暂时没有具体内容
class NewSpecialDish:pass # 这里是一个占位符,表示这个类目前没有具体的功能# 创建一个实例
dish = NewSpecialDish()# 打印实例类型
print(type(dish)) # 输出: <class '__main__.NewSpecialDish'>
-
类名
NewSpecialDish
:就像是新菜谱的名字,为未来的新菜品预留了位置。 -
pass
语句:就像是在菜谱中写了一个占位符,告诉其他厨师“这里有一个新菜品,但我们还没准备好具体的烹饪步骤”。
为什么需要 pass
-
预留位置:使用
pass
可以确保类的名称在当前代码中占有一席之地,即使类还没有具体的功能。 -
避免错误:Python 需要一个缩进的代码块来定义类或函数,如果没有缩进的代码块,会抛出
IndentationError
。pass
语句可以避免这种错误。 -
后续开发:在后续开发中,你可以回来填充这个类的具体内容,而不必担心代码结构的问题。
Python 通过缩进来定义代码块的结构。当解释器遇到像 def, class, if, for 这样的语句,并且后面跟着冒号 : 时,它就期望接下来会有一个或多个缩进的语句来构成这个代码块。如果它没有找到任何缩进的语句(即代码块是空的),它就无法确定这个结构的范围,因此会抛出 IndentationError。
pass 语句的存在就是为了解决这个问题:它本身不执行任何操作,但它是一个有效的 Python 语句。所以,当你需要一个语法上存在的代码块,但又暂时不想在其中放入任何实际的逻辑时,pass 就是一个完美的占位符,它告诉解释器:“这里有一个代码块,但它什么也不做。”
类的初始化方法:准备食材
初始化方法就像厨师在烹饪之前准备好所有需要的食材。
class ClassicDish:def __init__(self, main_ingredient, spice_level):self.main_ingredient = main_ingredient # 主食材self.spice_level = spice_level # 辣度def prepare_ingredients(self):print(f"准备 {self.main_ingredient},辣度设为 {self.spice_level}")dish = ClassicDish("鸡丁", "中辣")
dish.prepare_ingredients()
-
__init__
方法:就像厨师在开始烹饪前的准备工作,创建对象时自动调用。 -
self
参数:代表当前正在处理的“这道菜”。 -
main_ingredient
和spice_level
:是类的属性,就像菜谱中需要的主食材和辣度等级。
初始化方法又叫构造方法、特殊方法
类有2种方法
1. 初始化方法,
初始化方法可以理解为“准备食材”的过程。
class ClassicDish:def __init__(self, main_ingredient, spice_level):self.main_ingredient = main_ingredient # 主食材self.spice_level = spice_level # 辣度def prepare_ingredients(self):print(f"准备 {self.main_ingredient},辣度设为 {self.spice_level}")
-
初始化方法
__init__
:就像厨师在开始烹饪前的准备工作。当你点了一道菜(创建对象),厨师会先准备好所有的食材(初始化属性)。 -
self
参数:代表当前正在处理的“这道菜”。 -
main_ingredient
和spice_level
:是类的属性,就像菜谱中需要的主食材和辣度等级。 -
调用时:
dish = ClassicDish("鸡丁", "中辣") dish.prepare_ingredients() # 输出:准备 鸡丁,辣度设为 中辣
2. 普通放大
类的普通方法:烹饪步骤
普通方法就像菜谱中的具体烹饪步骤,只有在厨师执行这些步骤时才会进行实际的操作。
除了init方法(初始化方法,又名构造方法),还包含一些普通方法(自己定义)
普通方法和init方法的差别在于,init方法是类的构造方法,当创建对象时,会自动调用init方法----只要你创建这个类对象了,这个init函数就会执行;普通方法是只有你调用类的这个方法的时候,函数才会执行。
class ClassicDish:def __init__(self, main_ingredient, spice_level):self.main_ingredient = main_ingredientself.spice_level = spice_leveldef cook(self):print(f"正在烹饪 {self.main_ingredient},辣度 {self.spice_level}")def serve(self):print(f"{self.main_ingredient} 已经做好,可以端上桌了")dish = ClassicDish("鸡丁", "中辣")
dish.cook() # 输出:正在烹饪 鸡丁,辣度 中辣
dish.serve() # 输出:鸡丁 已经做好,可以端上桌了
-
cook
和serve
方法:是普通方法,只有在调用时才会执行。
类的继承-属性的继承、方法的继承:改良菜谱
继承就像在原有菜谱的基础上进行改良,保留原有的步骤,同时添加新的步骤或修改某些步骤。
类已经是比较优秀的封装了,封装了函数、封装了属性
正如装饰器进一步封装了函数的可复用的功能,装饰器函数封装了函数
那么有没有东西可以进一步封装类呢?这就引出了类的继承
class SpecialDish(ClassicDish):def __init__(self, main_ingredient, spice_level, special_topping):super().__init__(main_ingredient, spice_level)self.special_topping = special_topping # 新增的特殊配料def add_special_topping(self):print(f"添加特殊配料:{self.special_topping}")def cook(self):super().cook()self.add_special_topping()special_dish = SpecialDish("鸡丁", "微辣", "腰果")
special_dish.cook()
special_dish.serve()
-
SpecialDish
类:继承自ClassicDish
类,保留了原有的属性和方法,同时添加了新的属性和方法。 -
super()
函数:用于调用父类的构造方法,确保父类的初始化逻辑被执行。 -
add_special_topping
方法:是新增的方法,用于添加特殊配料。 -
重写
cook
方法:在原有cook
方法的基础上增加了新的步骤。
在面向对象编程中,继承允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码复用和功能扩展。子类可以:
1. 复用父类的代码(无需重新实现)。
2. 重写父类的方法(修改或增强功能)。
3. 添加新的方法和属性(扩展功能)。
作业
题目1:定义圆(Circle)类
要求:
- 包含属性:半径 radius。
- 包含方法:
- calculate_area():计算圆的面积(公式:πr²)。
- calculate_circumference():计算圆的周长(公式:2πr)。
- 初始化时需传入半径,默认值为 1。
# 示例运行
circle = Circle(5)
print(f"半径:{circle.radius}") # 输出:半径:5
print(f"面积:{circle.calculate_area()}") # 输出:面积:78.54(保留两位小数)
print(f"周长:{circle.calculate_circumference()}") # 输出:周长:31.42(保留两位小数)
import mathclass Circle:def __init__(self, radius=1):self.radius = radiusdef calculate_area(self):try:if self.radius < 0:return 0return math.pi * self.radius ** 2except TypeError:return 0def calculate_circumference(self):try:if self.radius < 0:return 0return 2 * math.pi * self.radiusexcept TypeError:return 0# 示例运行
circle = Circle(5)
print(f"半径:{circle.radius}") # 输出:半径:5
print(f"面积:{circle.calculate_area():.2f}") # 输出:面积:78.54(保留两位小数)
print(f"周长:{circle.calculate_circumference():.2f}") # 输出:周长:31.42(保留两位小数)
题目2:定义长方形(Rectangle)类
- 包含属性:长 length、宽 width。
- 包含方法:
- calculate_area():计算面积(公式:长×宽)。
- calculate_perimeter():计算周长(公式:2×(长+宽))。 is_square() 方法,判断是否为正方形(长 == 宽)。
- 初始化时需传入长和宽,默认值均为 1。
rect = Rectangle(4, 6)
print(f"长:{rect.length}, 宽:{rect.width}") # 输出:长:4, 宽:6
print(f"面积:{rect.calculate_area()}") # 输出:面积:24
print(f"周长:{rect.calculate_perimeter()}") # 输出:周长:20
print(f"是否为正方形:{rect.is_square()}") # 输出:是否为正方形:Falsesquare = Rectangle(5, 5)
print(f"是否为正方形:{square.is_square()}") # 输出:是否为正方形:True
class Rectangle:def __init__(self, length=1, width=1):self.length = lengthself.width = widthdef calculate_area(self):try:if self.length < 0 or self.width < 0:return 0return self.length * self.widthexcept TypeError:return 0def calculate_perimeter(self):try:if self.length < 0 or self.width < 0:return 0return 2 * (self.length + self.width)except TypeError:return 0def is_square(self):return self.length == self.width# 示例运行
rect = Rectangle(4, 6)
print(f"长:{rect.length}, 宽:{rect.width}") # 输出:长:4, 宽:6
print(f"面积:{rect.calculate_area()}") # 输出:面积:24
print(f"周长:{rect.calculate_perimeter()}") # 输出:周长:20
print(f"是否为正方形:{rect.is_square()}") # 输出:是否为正方形:Falsesquare = Rectangle(5, 5)
print(f"是否为正方形:{square.is_square()}") # 输出:是否为正方形:True
题目3:图形工厂
创建一个工厂函数 create_shape(shape_type, *args),根据类型创建不同图形对象:图形工厂(函数或类)
shape_type="circle":创建圆(参数:半径)。
shape_type="rectangle":创建长方形(参数:长、宽)。
shape1 = create_shape("circle", 5)
print(shape1.calculate_circumference()) # 输出:31.42shape2 = create_shape("rectangle", 3, 4)
print(shape2.is_square()) # 输出:False
import mathclass Circle:def __init__(self, radius=1):self.radius = radiusdef calculate_area(self):try:if self.radius < 0:return 0return math.pi * self.radius ** 2except TypeError:return 0def calculate_circumference(self):try:if self.radius < 0:return 0return 2 * math.pi * self.radiusexcept TypeError:return 0class Rectangle:def __init__(self, length=1, width=1):self.length = lengthself.width = widthdef calculate_area(self):try:if self.length < 0 or self.width < 0:return 0return self.length * self.widthexcept TypeError:return 0def calculate_perimeter(self):try:if self.length < 0 or self.width < 0:return 0return 2 * (self.length + self.width)except TypeError:return 0def is_square(self):return self.length == self.widthdef create_shape(shape_type, *args):if shape_type == "circle":if len(args) == 1:return Circle(args[0])else:raise ValueError("Circle requires exactly one argument: radius")elif shape_type == "rectangle":if len(args) == 2:return Rectangle(args[0], args[1])else:raise ValueError("Rectangle requires exactly two arguments: length and width")else:raise ValueError("Unsupported shape type")# 测试
shape1 = create_shape("circle", 5)
print(shape1.calculate_circumference()) # 输出:31.41592653589793shape2 = create_shape("rectangle", 3, 4)
print(shape2.is_square()) # 输出:False
@浙大疏锦行