31天Python入门——第17天:初识面向对象

| |
文章目录
- 面向对象编程
- 1. 什么是面向对象
- 2. 类(class)
- 3. 类的实例
- 关于self
- 4. 对象的初始化
- 5. `__str__`
- 6. 类之间的关系
- 继承关系
- 组合关系
- 7. 补充练习
面向对象编程
1. 什么是面向对象
面向对象编程是一种编程思想,它将现实世界的概念和关系映射到代码中.
在面向对象编程中,我们通过创建对象来表示现实世界中的事物,并通过定义对象的属性和方法来描述它们的状态和行为.
面向对象编程强调了代码的模块化、封装、抽象、继承和多态等概念.
例如, 在现实世界中我们需要去记录一名学生的基本信息, 如果使用文本来记录: 例如
有一名学生叫张三, 来自北京, 性别男. 这样可以轻松记录一名学生的信息.
但是使用如下表格, 结构会更加的清晰.
| 姓名 | 张三 |
|---|---|
| 年龄 | 20 |
| 籍贯 | 北京 |
| 性别 | 男 |
在现实世界中我们需要去记录一辆车的基本信息, 需要用到如下表格:
| 品牌 | |
|---|---|
| 型号 | |
| 排量 | |
| 车架号 |
上述都表格都可以很清晰的描述学生和车辆的基本信息.
表格相当于是 一个蓝图或者模板. 每个学生都可以拿着这个表格, 填写自己对应的信息, 在程序中, 上面的表格就相当于是一个类, 通过这个类可以创建多个学生对象.
2. 类(class)
类和对象是面向对象编程的核心概念.
类是一个抽象的概念,用于描述对象的属性(数据)和方法(行为).
对象则是类的具体实例,表示一个具体的实体.
类(class)
类是一种模板或蓝图,用于创建对象.它定义了对象的属性和方法,描述了对象的状态和行为.类通过定义一组相关的属性和方法来封装数据和操作,从而提供了一种组织和管理代码的方式.
Python 中的一切对象都有各自的类型,比如:
整数对象 的类型是 int
字符串对象 的类型是 str
列表对象 的类型是 list
元组对象 的类型是 tuple
字典对象 的类型是 dict
Python 的内置函数type可以查看对象的类型:
>>> type(12)
<class 'int'> # 整数类型
>>> type('12')
<class 'str'> # 字符类型
>>> type([1,2])
<class 'list'> # 列表类型
>>> type((1,2))
<class 'tuple'> # 元组类型
>>> type({1:2})
<class 'dict'> # 字典类型
我们掌握了这些内置的数据类型,通常就可以开发Python程序了.
但是当我们要开发的软件系统 更加复杂的时候,尤其是系统里面的对象 和现实世界的对象 存在对应关系的时候,如果只能用这些内置类型,就会感觉很不方便.
比如,我们的程序要表示一个 奔驰汽车 这样的对象类型,属性有:品牌,国家,价格.
如果只用Python的内置类型,大家想想怎么表示.
当然,我们可以定义一个字典类型的对象,比如:
benzCar = {'brand' : '奔驰','country' : '德国','price' : 300000
}
如果这个汽车对象还需要有自己特定的行为,比如 按喇叭会发出嘟嘟的声音。那又该怎么定义呢?
有人说,可以定义一个函数对象作为它的属性,像这样:
def pressHorn():print('嘟嘟~~~~~~')benzCar = {'brand' : '奔驰','country' : '德国','price' : 300000,'pressHorn' : pressHorn # 字典对象的值可以是一个函数对象
}# 我可以这样执行它的行为
benzCar['pressHorn']()
似乎也可以.
但是这里 benzCar 更像是一个具体的对象,并不是一种 对象类型.
而且 这个 benzCar 汽车的 行为的定义 ,要在外面定义一个函数, 然后benzCar字典的内部去引用它,这样也比较麻烦.
为了解决这样的普遍问题,Python语言可以让我们 自己定义对象类型.
Python中自定义对象类型,就是 定义一个类 , 类 就是 类型 的意思.
比如 : 奔驰汽车, 可以这样定义:
使用 class 关键字定义一个类.类名通常使用大写字母开头,遵循大驼峰命名规范.
class BenzCar: brand = '奔驰' # 品牌属性country = '德国' # 产地属性@staticmethoddef pressHorn(): print('嘟嘟~~~~~~')
定义一个类 用关键字 class 后面加 类的名称.
类名的规范 和 变量命名规范一样。 通常我们会把类名 首字母大写, 这里定义的类名就是 BenzCar
下面定义的 brand, country 都是 BenzCar 类的 属性.
这种属性被称为类属性
如果我们要得到属性的值可以这样用 类名.属性名 的方式,如下
print(BenzCar.brand)
而 pressHorn 则是该类型的一个 方法.
请注意上面的 @staticmethod 的修饰, 说明这是该类的一个 静态方法
要调用执行该类的静态方法,像这样就可以了:
BenzCar.pressHorn()
类的属性
类的属性是与类相关联的数据,用于描述对象的特征或状态.
类的属性可以通过类名或对象访问.类属性在类级别上定义,被所有类的实例共享.
类属性的定义实际上就是写在类中的变量. (成员变量) - 注意不是实例属性哦
class Student:name = '张三'age = 0
在上面的示例中, Student类定义了2个类属性, name和age,它被所有Student类的实例共享. 类属性可以通过类名或者实例对象访问.
class Student:name = '张三'age = 18# 类属性可以通过**类名或者实例对象**访问print(Student.name)print(Student.age)print("-----------------------------")stu = Student()print(stu.name)print(stu.age)
类的方法
类的方法是与类相关联的函数,用于定义对象的行为.方法在类中定义,并通过对象调用.方法可以访问和操作对象的属性.
class Student:name = '张三'age = 0def introduce(self):print(f'大家好, 我的名字叫{self.name}, 今年{self.age}岁')def study(self):print(f'{self.name}正在努力学习.')stu = Student()stu.introduce()stu.study()
以上示例在Student类中定义了2个方法, introduce和study. 用于定义对象的2个行为: 自我介绍和学习.
上述示例中方法中第一个形参是self的方法叫做实例方法. 类属性可以通过实例对象self来访问.
3. 类的实例
Python中 类 是 某种对象的类型.
比如 int 是 整数对象的类型, str是字符串对象的类型, list是 列表对象的类型.
我们把一个个具体的 对象称为 该类型的 实例,
比如,我们可以说
数字对象 3 是 int 类型的的实例,具有int类型的特征
字符串对象 ‘abc’ 是 str 类型的实例,具有str类型的特性(比如可以执行str的所有方法,比如 find, split等)
列表对象 [1,2,3] 是 list 类型的的实例,具有list类型的特性(比如可以执行list的所有方法,比如 reverse,append等)
同样的,我们自定义的类,也可以产生该类的实例对象. 每个实例对象就是该类的一个实例,具有该类的一切特征.
要产生一个类的实例对象,只需要 在类名后面加上括号,就可以了,就会返回一个该类的实例对象.
比如:
car1 = BenzCar()
car1 变量就对应了一个 BenzCar 类型 的实例对象,具有 BenzCar 类的一切属性和方法.大家可以执行下面的代码试试。
class BenzCar: brand = '奔驰' country = '德国' @staticmethoddef pressHorn(): print('嘟嘟~~~~~~')car1 = BenzCar()
print(car1.brand)
car1.pressHorn()
同样,我们也可以用 type 函数查看 car1 这个实例的类型,如下所示:
>>> type(car1)
<class '__main__.BenzCar'>
说明 car1 是 __main__ 模块里面定义的 BenzCar 类型.
大家一定要搞清楚 类 和 实例 的关系.
比如 :
人 就是 一个 类, 而 关羽、张飞 就是 人 这个类的 具体实例.
狗 也是 一个 类, 而 你们家的阿黄 和 隔壁家的旺财 就是狗 这个类的 具体实例.
Python中 定义一个类型 就是描述 这些类型的实例的 公共特征. 后面根据这个类创建的实例 都具有这个类的 特征,就是 具体什么 属性、方法.
在面向对象编程中,类的对象(Class Object)是类的具体实例, 所以类的对象也叫做实例对象.类定义了对象的属性和方法,而对象是类的实体,具有自己的属性值和对类中方法的访问权限.
实例属性和实例方法
刚才我们定义的类里面的属性都是 类属性 ,里面的方法都是类的 静态方法 .
所有BenzCar类的实例对象,其 品牌名 brand ,对应的类属性应该是相同的.
就是说下面这样的两个实例:
car1 = BenzCar()
car2 = BenzCar()
car1 和 car2 的 brand属性 都是一样的 值, 都是字符串 ‘奔驰’
很好理解,因为品牌这样的属性 对于所有的 奔驰车都是一样的,都是 ‘奔驰’.
类属性 是类的共同特征属性.
但是有些属性,比如颜色、发动机编号 是每一辆奔驰车 都不同的.
所以,在我们定义的 类BenzCar 里面, 颜色、发动机编号 是 不应该 作为类属性的.
每个实例独有的属性,称之为 类的实例属性
实例属性通常是在类的 初始化方法 init 里面定义的.
比如:
class BenzCar: brand = '奔驰' country = '德国' @staticmethoddef pressHorn(): print('嘟嘟~~~~~~')# 初始化方法, 注意前后各有两个下划线def __init__(self):self.color = 'red' # 颜色self.engineSN = '837873398' # 发动机编号
上面的初始化方法 __init__ ,就创建了两个实例属性 color 和 engineSN。
为什么 __init__ 方法 叫初始化方法呢?
解释器在执行 像下面这样的 实例化类对象 的代码时,
car1 = BenzCar()
首先,解释器会 在内存中 创建一个该类 的 实例对象;
然后,解释器会查看这个类是否有 __init__方法,如果有,就会去调用它.
__init__ 是 创建好实例后 立即就要 执行 的方法,所以称之为初始化方法.
通常我们会在__init__方法里面 执行一些初始化的动作,主要就是创建该实例的 实例属性.
__init__ 方法的第一个参数是 self, 它 是干什么用的呢?
刚才说了, 解释器执行实例化代码,会先在内存中创建该类实例对象,然后调用类 的__init__方法.
调用 __init__方法时,就将实例对象 传递给 self参数.
self 参数变量 指向的 就是 实例对象 本身, 所以下面的代码就是创建该实例的属性color 和 engineSN 了
self.color = 'red' # 颜色
self.engineSN = '8378738398' # 发动机编号
类的静态方法要在方法定义 上面加上 @staticmethod 的修饰.
而 类的 实例方法 不需要任何修饰.
通常类的实例方法,都是要 访问类的实例属性的. 包括: 创建、修改、删除 类的实例属性.
因为 实例方法 就是要操作 实例独有的属性,否则不操作任何实例属性的话,就应该定义为 类方法.
比如 __init__ 初始化方法,就是一个实例方法,它通常要创建一些实例属性.
而 pressHorn 方法是类的静态方法, 静态方法是不能访问实例属性的.
有时候,实例属性的取值,不是固定写在初始化方法的代码里面.
比如这里,每辆车的颜色、发动机号都是不同的,我们应该作为参数传进去.
所以修改代码为这样:
class BenzCar: brand = '奔驰' country = '德国' @staticmethoddef pressHorn(): print('嘟嘟~~~~~~')def __init__(self,color,engineSN):self.color = color # 颜色self.engineSN = engineSN # 发动机编号
这样我们在创建实例的时候,就可以根据需要指定不同的实例属性了,比如:
car1 = BenzCar('白色','24503425527866')
car2 = BenzCar('黑色','34598423586877')
print(car1.color)
print(car2.color)
print(car1.engineSN)
print(car2.engineSN)
虽然定义的时候, init 方法 有3个参数 : self,color,engineSN
但是我们这样调用 BenzCar() 实例化的时候, 只需要传入后面两个参数即可,
因为self 参数 需要传入实例对象本身,解释器会自动帮我们传入.
其它的 实例方法也是这样, 比如我们定义一个 修改车身颜色的方法 changeColor:
class BenzCar: brand = '奔驰' country = '德国' @staticmethoddef pressHorn(): print('嘟嘟~~~~~~')def __init__(self,color,engineSN):self.color = color # 颜色self.engineSN = engineSN # 发动机编号def changeColor(self,newColor):self.color = newColorcar1 = BenzCar('白色','24503425527866')
car1.changeColor('黑色')print (car1.color)
调用 changeColor方法的时候,只需要传入参数 newColor 对应新的颜色即可.
不需要我们传入self参数,self 参数是实例对象本身,解释器会自动帮我们传入.
注意: 如果你的实例属性名称 和 静态属性(类属性) 重复了 ,通过类实例访问该属性,访问的是实例属性通过类名访问该属性,访问的是类属性.
比如:
class Car:brand = '奔驰'name = 'Car'def __init__(self):# 可以通过实例访问到类属性print(self.brand)# 定义实例属性和类属性重名self.name = 'benz car'c1 = Car()print(f'通过实例名访问name:{c1.name}')
print(f'通过类名 访问name:{Car.name}')
一旦创建了 和类属性同名的 实例属性,通过实例访问的就是实例属性了
实例方法是指定义在类中的方法, 它可以访问和操作对象的实例属性, 并且在调用时会自动传入对象自身(通常用 self 来表示)作为第一个参数.
实例方法是与类的实例(对象)关联的, 并且可以访问和修改对象的状态.
使用实例方法时, 需要先创建类的实例(对象), 然后通过对象调用方法, 在调用实例方法时, 不需要手动传入 self 参数, Python会自动将对象本身传递给方法.
关于self
在实例方法中, 第一个参数通常是 self, 表示对象自身.定义实例方法时, 必须将 self 作为方法的第一个参数.通过 self, 方法可以访问对象的属性和其他方法.- 在创建类的实例(对象)时, Python会自动传递 self 参数给实例方法, 不需要手动传入.当调用对象的方法时, 无需显式传递 self, Python会自动将对象本身传递给方法.
- 在实例方法内部, 可以通过 self 来访问对象的属性和方法.例如,
self.attribute可以访问对象的属性,self.method()可以调用对象的其他方法
4. 对象的初始化
__init__ 是Python中一个特殊的方法, 用于初始化对象的属性.它是在创建对象时自动调用的构造方法, 也叫做魔法方法.
在类的定义中, __init__ 方法用于初始化对象的属性.通常, 在创建对象时需要对对象的属性进行初始化操作, 比如设置默认值或接收外部传入的参数.
__init__ 方法的命名是固定的, 必须使用双下划线 __ 前缀和后缀.在调用类创建对象时, Python会自动调用类的 __init__ 方法来初始化对象的属性.
注意: __init__方法只能返回None, 不能有其他返回值.
实例属性
实例属性是指定义在类的实例(对象)中的属性, 每个对象都有自己独立的实例属性.实例属性用于存储对象的状态和数据, 并且在类的实例化过程中被赋予特定的值.
在Python中, 实例属性通常是在类的构造方法 __init__ 中使用 self 关键字定义的.(类的实例属性在类外(或者说是__init__方法外定义的实例属性)也有定义的情况)
每个实例属性都是一个对象独有的变量, 不同的对象之间互不干扰.
class Student:def __init__(self, name, age):self.name = nameself.age = agedef introduce(self):print(f"大家好, 我的名字叫{self.name}, 今年{self.age}岁")self.study_course('英语')def study_course(self, course):print(f"{self.name}正在努力学习{course}")stu1 = Student("李四", 18)
stu1.introduce()
stu1.study_course("english")stu2 = Student("张三", 20)
stu2.introduce()
stu2.study_course("maths")
实例属性和类属性总结
-
定义位置:
- 实例属性:实例属性是定义在类的方法中(通常是在构造函数
__init__中)通过self关键字定义的,每个实例(对象)都有自己的一份实例属性. 类属性:类属性是定义在类的方法之外的属性, 直接在类的内部定义的, 属于整个类, 所有实例共享同一份类属性.
- 实例属性:实例属性是定义在类的方法中(通常是在构造函数
-
存储位置:
-
实例属性:每个实例(对象)都有自己独立的实例属性, 存储在对象中.
-
类属性:类属性属于整个类, 存储在类中.
-
-
值的独立性:
- 实例属性:不同实例的同名实例属性是相互独立的, 一个实例的实例属性修改不会影响其他实例.
- 类属性:所有实例共享同一份类属性, 但是通过一个实例修改类属性时, 这个修改不会影响其他实例的类属性.类属性是属于类的, 而不是属于实例的, 因此每个实例都拥有独立的类属性副本, 互不影响.
-
访问方式:
-
实例属性:通过对象访问, 使用表达式
对象名.属性名. -
类属性:可以通过类名访问, 也可以通过对象访问.使用表达式
类名.属性名或对象名.属性名.
-
5. __str__
__str__是Python中的特殊方法(魔法方法), 用于定义类的实例对象的字符串表示.
当我们使用print函数或str()函数打印一个类的实例对象时, 实际上是调用了该对象的__str__方法来获取其字符串表示.
如果在类中定义了__str__方法, 那么当我们打印该类的实例时, 会输出__str__方法返回的字符串.这对于自定义类的字符串表示非常有用, 可以让我们以更加直观和可读的方式展示对象的内容.
主要用途包括:
- 打印:当你使用
print函数打印一个对象时, 实际上是调用该对象的__str__方法来获取其字符串表示, 从而以更直观和易读的方式显示对象的信息. - 字符串转换:当你使用
str()函数来将一个对象转换为字符串时, 同样会调用该对象的__str__方法, 以获得其字符串表示. - 字符串格式化:在字符串格式化时, 如果包含了对象, Python会自动调用对象的
__str__方法, 以便获取对象的字符串表示.
class Student:# 类属性gender = '男'def __init__(self, name, age):# 实例属性self.name = nameself.age = agedef introduce(self):print(f"大家好, 我的名字叫{self.name}, 今年{self.age}岁")self.study_course()def study_course(self, course="英语"):print(f"我正在努力学习{course}")def __str__(self):return f"学生: {self.name}, {self.age}, {self.gender}"# 1. 打印
stu = Student("李四", 18)
print(stu)
# 2. 字符串转换
stu_string1 = str(stu)
print(stu_string1)
# 3. 字符串格式化
stu_string2 = f'{stu}'
print(stu_string2)# 输出结果:
学生: 李四, 18, 男
学生: 李四, 18, 男
学生: 李四, 18, 男
6. 类之间的关系
继承关系
真实世界中,类型之间 可能存在 范围 包含关系.
比如:人 这个类型 和 亚洲人 这个类型.
人 是包括了 亚洲人 的。 如果 某人 是一个 亚洲人,那么它必定是一个 人.
这种关系,编程语言中称之为 继承关系.
比如上面的例子, 亚洲人 这个类 就 继承 了 人 这个类.
通常我们把被继承的类称之为 父类 或者叫 基类.
把继承类称之为 子类 或者 派生类.
同样的,以车为例, 上面我们定义了奔驰车 这个类, 我们还可以定义两个 子类: 奔驰2016 和 奔驰2018 对应两种不同款的奔驰车.
如下所示:
class BenzCar: brand = '奔驰' country = '德国' @staticmethoddef pressHorn(): print('嘟嘟~~~~~~')def __init__(self,color,engineSN):self.color = color # 颜色self.engineSN = engineSN # 发动机编号def changeColor(self,newColor):self.color = newColorclass Benz2016(BenzCar):price = 580000model = 'Benz2016' class Benz2018(BenzCar):price = 880000model = 'Benz2018'
大家可以发现定义子类的时候,必须指定它的父类是什么.
指定的方法就是在类名的后面的括号里写上父类的名字.
大家注意: 子类会自动拥有父类的一切属性和方法
为什么? 因为一个子类的实例对象 ,必定也是一个父类的实例对象. 当然需要拥有父类的一切属性和方法.
就像 一个亚洲人 当然 拥有一个 人 所应该具有的一切特性.
比如,执行下面的代码:
car1 = Benz2016('red','234234545622')
car2 = Benz2018('blue','111135545988') print (car1.brand)
print (car1.country)
car1.changeColor('black')print (car2.brand)
print (car2.country)
car2.pressHorn()
输出结果如下:
奔驰
德国
奔驰
德国
嘟嘟~~~~~~
一个子类在继承父类的一切特性的基础上,可以有自己的属性和方法.
比如:
class Benz2018(BenzCar):price = 880000model = 'Benz2018' def __init__(self,color,engineSN,weight):# 先调用父类的初始化方法BenzCar.__init__(self,color,engineSN)self.weight = weight # 车的重量self.oilweight = 0 # 油的重量# 加油def fillOil(self, oilAdded):self.oilweight += oilAdded self.weight += oilAdded
这里 子类 Benz2018 ,新增了两个 类属性
价格: price
型号: model
新增了两个实例属性
整车重量:weight
油的重量:oilweight
新增了一个实例方法 fillOil , 对应 加油这个行为.
这个行为会导致 实例属性 weight 和 oilweight 变化,所以必须是 实例方法.
这样定义好了以后, 就可以创建该类的实例,并访问其新的方法和属性了.
car2 = Benz2018('blue','111135545988',1500)
print (car2.oilweight)
print (car2.weight)
car2.fillOil(50)
print (car2.oilweight)
print (car2.weight)
要特别注意的是, 子类的初始化方法里面,如果有一部分的初始化代码和父类的初始化相同(通常都是这样),需要显式的 调用父类的初始化方法 __init__
而且要传入相应的参数, 像上面那样,然后可以加上自己的特有的初始化代码. 如下所示:
def __init__(self,color,engineSN,weight):# 先调用父类的初始化方法BenzCar.__init__(self,color,engineSN)self.weight = weight self.oilweight = 0
如果子类 没有 自己的初始化方法,实例化子类对象时,解释器会自动调用父类初始化方法,如下:
class Rect:def __init__(self):print('初始化 rect')class Squre(Rect):passs = Squre()
运行结果,会打印出 ‘初始化 rect’
但是,如果子类 有自己 的初始化方法,实例化子类对象时,解释器就不会自动化调用父类的初始化方法,如下
class Rect:def __init__(self):print('初始化 rect')class Square(Rect):def __init__(self):print('初始化 square')s = Squre()
运行结果只会打印 初始化 square.
调用父类的方法,除了直接用父类的名字 BenzCar, 还可以使用 函数 super()
像这样:
def __init__(self,color,engineSN,weight):# 同样是调用父类的初始化方法super().__init__(color, engineSN)self.weight = weight self.oilweight = 0
这样使用的时候,方法参数中 不需要加上 self 参数.
使用 super 的好处之一就是:子类中调用父类的方法,不需要 显式指定 父类的名字. 代码的可维护性更好.
想象一下,如果 BenzCar 有很多子类,如果哪一天 BenzCar 类改了名字,采用 super 这样的写法,就不需要修改子类的代码了.
注意 super不仅仅可以调用父类的初始化方法,也可以调用父类的其他方法.
一个子类,同时还可以是另一个类的父类,
比如 亚洲人 可以是 人 的子类, 同时可以是 中国人 的父类.
因为一个中国人,一定是一个亚洲人, 当然也一定是一个 人.
同样的,上面的车的例子, 我们还可以定义 奔驰2018混合动力 作为 奔驰2018 的 子类.
定义的语法还是一样的:
class Benz2018Hybrid(Benz2018):model = 'Benz2018Hybrid' price = 980000def __init__(self,color,engineSN,weight):Benz2018.__init__(self,color,engineSN,weight)
同样,类 Benz2018Hybrid 也会拥有其父类 Benz2018 的一切属性和方法,自然也包括 父类的父类 BenzCar 的一切属性和方法
car2 = Benz2018Hybrid('blue','111135545988',1500)
print (car2.oilweight)
print (car2.weight)
car2.fillOil(50)
print (car2.oilweight)
print (car2.weight)
组合关系
除了上面的继承关系, 类之间还有一种常见的组合关系.
所谓组合关系,就是一个类实例的属性里面包含另外一个类实例.
比如:
class BenzCar: brand = '奔驰' country = '德国' def __init__(self,color,engineSN):self.color = color # 颜色self.engineSN = engineSN # 发动机编号
这样的定义,类 BenzCar 中
brand 属性就是一个字符串对象 奔驰
country 属性就是一个字符串对象 德国
而该类的实例对象中,就包含了 两个属性 color 和 engineSN, 都是字符串对象
我们可以说 该类由 一些字符串对象 组合 而成.
甚至还可以包含 我们自己定义的类的实例,比如:
# 轮胎
class Tire: def __init__(self,size,createDate):self.size = size # 尺寸self.createDate = createDate # 出厂日期class BenzCar: brand = '奔驰' country = '德国' def __init__(self,color,engineSN,tires):self.color = color # 颜色self.engineSN = engineSN # 发动机编号self.tires = tires# 创建4个轮胎实例对象
tires = [Tire(20,'20160808') for i in range(4)]
car = BenzCar('red','234342342342566',tires)
上面的例子里,奔驰汽车对象就 包含 了4个轮胎 Tire 对象.
我们可以说奔驰汽车对象是由 4个轮胎对象 组合 而成,形成了对象的组合关系.
对象的 属性 变量 保存了 组合它的那些对象.
组合关系,可以通过上层对象的属性一步的访问到内部对象的属性
比如,我们可以通过 BenzCar 对象 访问其内部的轮胎对象
print(car.tires[0].size)
Python解释器对这个表达式 car.tires[0].size 是从左到右依次执行的,如下所示:
car.tires # BenzCar实例的tires属性,得到一个列表,里面是四个 Tire 实例对象car.tires[0] # 得到BenzCar实例的tires属性列表里面的第1个Tire 实例对象car.tires[0].size # 得到BenzCar实例的tires属性列表里面的第1个Tire 实例对象的size属性
7. 补充练习
"""
创建一个简单的学生管理系统,包含以下功能:定义一个Student类,包含以下属性:学号(number),姓名(name),年龄(age),性别(gender)和成绩(score)。实现类的初始化方法__init__,用于初始化学生的属性。实现类的__str__方法,用于返回学生信息的字符串表示,格式为:学号:[学号],姓名:[姓名],年龄:[年龄],性别:[性别],成绩:[成绩]。定义一个学生管理类StudentManager,用于管理学生信息。该类应该包含以下功能:添加学生:能够添加一个学生的信息到学生列表中。
显示所有学生:能够打印出所有学生的信息。
因为之前我们已经做过了类似的管理系统, 所以这里就不再做很复杂的功能, 只做两个小功能达到练习的目的即可.
"""# 学生类
class Student:def __init__(self, number, name, age, gender, score):self.number = numberself.name = nameself.age = ageself.gender = genderself.score = scoredef __str__(self):return f"学号:[{self.number}],姓名:[{self.name}],年龄:[{self.age}],性别:[{self.gender}],成绩:[{self.score}]"# 学生管理类
class StudentManager:def __init__(self):self.stu_list = []def add_stu(self, student):self.stu_list.append(student)print(f'添加学生{student.name}成功')def show_all_stu(self):for stu in self.stu_list:print(stu)def main():# 主流程student_manager = StudentManager()print('欢迎进入学生管理系统')while True:print('1: 添加学生')print('2: 展示所有学生')print('0: 退出系统')choice = int(input("请输入操作编号: "))if choice == 1:number = input('请输入学生学号')name = input('请输入学生姓名')age = input('请输入学生年龄')gender = input('请输入学生性别')score = input('请输入学生分数')student = Student(number, name, age, gender, score)student_manager.add_stu(student)elif choice == 2:student_manager.show_all_stu()elif choice == 0:print(f"退出学生管理系统")breakelse:print('您输入的指令是无效指令')if __name__ == '__main__':main()
| |
| |
相关文章:
31天Python入门——第17天:初识面向对象
你好,我是安然无虞。 文章目录 面向对象编程1. 什么是面向对象2. 类(class)3. 类的实例关于self 4. 对象的初始化5. __str__6. 类之间的关系继承关系组合关系 7. 补充练习 面向对象编程 1. 什么是面向对象 面向对象编程是一种编程思想,它将现实世界的概念和关系映…...
计算机视觉准备八股中
一边记录一边看,这段实习跑路之前运行完3DGAN,弄完润了,现在开始记忆八股 1.CLIP模型的主要创新点: 图像和文本两种不同模态数据之间的深度融合、对比学习、自监督学习 2.等效步长是每一步操作步长的乘积 3.卷积层计算输入输出…...
【C语言】文件操作(2)
一、文件的随机读写 在前面我们学习了文件的顺序读写的函数,那么当我们要读取某个指定位置的内容的时候,是否只能顺序的读取到这个内容?还有在对文件进行输入的时候,需要对指定的位置进行写入,那么此时应该怎么办呢&a…...
CCCC天梯赛L1-094 剪切粘贴
题目链接: 字符串函数: 1、截取字符串: //起始位置为3,结束位置为5string s "aabcdefg";//下标从0开始 [从开始位置,结束位置]string sub s.substr(3,3);//输出cde, 有返回值string//并且原字符串不改变, s"aab…...
C语言:多线程
多线程概述 定义 多线程是指在一个程序中可以同时运行多个不同的执行路径(线程),这些线程可以并发或并行执行。并发是指多个线程在宏观上同时执行,但在微观上可能是交替执行的;并行则是指多个线程真正地同时执行&…...
livekit ICE连接失败的一些总结
在使用livekit做的项目过程中碰到了一些ICE连接失败的问题, 一个时在同网段的局域网下 ,livekti服务和客户端不能联通,后来发现是服务端是多网卡,通过网络抓包才知道服务端在stun binding的时候使用了错误的网卡,在co…...
Python神经网络1000个案例算法汇总
【2025最新版】Python神经网络优化1000个案例算法汇总(长期更新版) 本文聚焦神经网络、优化算法,神经网络改进,优化算法改进,优化算法优化神经网络权重、超参数等,现在只需订阅即可拥有,简直是人工智能初学者的天堂。…...
某地81栋危房自动化监测试点项目
1. 项目简介 房屋进入老龄化阶段后,结构安全风险越来越大。近10年来,每年都会产生房屋倒塌人员伤亡的重大安全事故。调研分析显示,老旧房屋结构安全风险管理的有效路径为,通过“人防技防”的组合模式,对房屋安全风险进…...
远程装个Jupyter-AI协作笔记本,Jupyter容器镜像版本怎么选?安装部署教程
通过Docker下载Jupyter镜像部署,输入jupyter会发现 有几个版本,不知道怎么选?这几个版本有什么差别? 常见版本有: jupyter/base-notebookjupyter/minimal-notebookjupyter/scipy-notebookjupyter/datascience-notebo…...
python文件的基本操作和文件读写
目录 文件的基本操作 文件读写 文件的基本操作 Python 中对文件的基本操作主要包括打开文件、读取文件、写入文件和关闭文件等操作。下面是一个简单的示例: 打开文件: file open(example.txt, r) # 使用 open() 函数打开一个名为 example.txt 的文…...
山东大学软件学院项目创新实训开发日志(4)之中医知识问答数据存储、功能结构、用户界面初步设计
目录 数据库设计: 功能设计: 用户界面: 数据库设计: --对话表 (1个对话包含多条消息) CREATE TABLE conversations ( conv_id VARCHAR(36) PRIMARY KEY, -- 对话ID user_id VARCHAR(36) NOT NULL, -- 所属用户 title VARCHAR(100), -- 对话…...
20.思科交换机二层链路聚合的详细配置命令解析
思科交换机二层链路聚合的详细配置命令解析 一、PAgP协议的配置SW1的配置SW2的配置二、LACP标准协议三、配置聚合组的带宽和速率四、确保所有接口的双工模式和速率一致五、故障排除和监控在Cisco设备上配置链路聚合(也称为端口通道或EtherChannel)可以增强网络连接的带宽和可…...
【FreeRtos】随手记录想法和DeepSeek的交流
纯记录个人RTOS学习过程和DeepSeek的交流,或记录一些学习过程中奇怪的想法(也会喂给deepseek哈哈) 2025/3/31 1. prvCreateTask在干啥? Question prvTaskCreate这个函数做了什么:分配内存,首先会判断栈…...
【多线程】单例模式和阻塞队列
目录 一.单例模式 1. 饿汉模式 2. 懒汉模式 二.阻塞队列 1. 阻塞队列的概念 2. BlockingQueue接口 3.生产者-消费者模型 4.模拟生产者-消费者模型 一.单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,其核心思想是确保…...
Qt5.14.2+Cmake使用mingw64位编译opencv4.5成功图文教程
一、下载安装相关编译环境软件 1.1 Python3.8:安装路径:C:\Users\Administrator\AppData\Local\Programs\Python\Python38-32 安装包:python3.8.exe 1.2 QT5.14.2:安装路径:C:\Qt\Qt5.14.2 1.3 opencv4.5:解压路径D:\o…...
Transformer习题
(1) 自注意力机制的特点: 并行计算:可同时处理序列中所有位置的关联,避免RNN的时序依赖问题。长距离依赖建模:直接捕捉序列中任意两个元素的关系,不受距离限制。动态权重分配:通过查询(Query&a…...
Mamba4D阅读
CVPR 2025 创新 基于transformer的4D主干由于其二次复杂度而通常存在较大的计算成本,特别是对于长视频序列。 开发了帧内空间Mamba模块,建立时空相关性。 GPU占用和速度很有优势。 代码还没发。 Pipeline 输入点云序列,根据超参数构建点管…...
uWebSockets开发入门
一、常用C++ WebSocket开源库 一些常用的 C++ WebSocket 开源库,它们支持 WebSocket 协议的实现,适用于客户端或服务器端开发。 1. Boost.Beast (推荐) 特点:基于 Boost.Asio 的高性能库,支持 HTTP/WebSocket,属于 Boost 官方库的一部分,稳定且跨平台。 适用场景:需要高…...
x265不同preset级别控制的编码参数与编码性能影响
目录 x265中preset 实验preset效果 写在最后 x265中preset 定义:preset是x265中用于平衡编码速度与压缩效率的核心参数。通过预定义的多组编码参数组合,用户无需手动调整复杂选项即可快速选择合适的编码模式。preset控制的参数(具体参数含义解析可参考专栏中相关博客)pr…...
Qt图形化界面为何总被“冷落“?
在Qt开发者的IDE中,Qt Designer总像一个被遗忘的角落——即便它有着直观的拖拽式界面设计功能。通过分析GitHub上超过5000个Qt项目发现,仅有17%的项目使用.ui文件构建界面。这个数据背后,隐藏着开发者群体对GUI构建方式的集体选择。我们不禁要…...
手工排查后门木马的常用姿势
声明!本文章所有的工具分享仅仅只是供大家学习交流为主,切勿用于非法用途,如有任何触犯法律的行为,均与本人及团队无关!!! 1. 检查异常文件 (1)查找最近修改的文件 # 查…...
算法导论(动态规划)——简单多状态
算法思路(17.16) 状态表示: 在处理线性动态规划问题时,我们可以通过“经验 题目要求”来定义状态表示。通常有两种选择: 以某个位置为结尾的情况;以某个位置为起点的情况。 本题中,我们选择更常…...
Linux 部署 rocketmq centos7
mq部署方案 1、rocketmq 顺序消费记录 一个master ,一个 brocker ,多个group ,多个topic,采用集群消费模式。 注意 一个group 对应一个 topic。 生产者 和 消费者 可以有多个,但是 主题和分组 都是一对一的。这样保证…...
LeetCode 438. 找到字符串中所有字母的异位词
438. 找到字符串中所有字母的异位词 题目描述 给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。 输入输出示例及数据范围 思路 这道题的思路其实很简单,就是一个滑动窗口的裸题&a…...
C++STL---<functional>
C标准库中的 <functional> 库是一个强大的工具集,它提供了用于处理函数对象、函数绑定、函数包装等功能的设施,极大地增强了代码的灵活性和可复用性。 1. 函数对象(Functors) 函数对象,也被称作仿函数…...
java详细笔记总结持续完善
一.Java开发环境的搭建 1. 单位换算 1TB 1024GB 1GB 1024MB 1MB 1024KB 1KB 1024Byte (字节) 1Byte 8 bit(位) 注意:一个字节占8位 2. DOS命令 DOS : Disk Operation System 磁盘操作系统 即用于操作本地磁盘的系统 命令操作符号盘符切换命令盘符名:查看当前文…...
图解AUTOSAR_SWS_CANInterface
AUTOSAR CAN接口详解文档 基于AUTOSAR标准的CAN通信接口模块架构与工作原理 目录 1. AUTOSAR CAN接口概述2. CAN接口架构 2.1 模块定位与组成2.2 内部组件结构2.3 接口关系3. CAN消息传输流程 3.1 消息发送流程3.2 消息接收流程4. CAN控制器模式管理 4.1 状态定义4.2 状态转换4…...
wsl2的centos7安装jdk17、maven
JDK安装 查询系统中的jdk rpm -qa | grep java按照查询的结果,删除对应版本 yum -y remove java-1.7.0-openjdk*检查是否删除 java -version 下载JDK17 JDK17,下载之后存到wsl目录下(看你自己)然后一键安装 sudo rpm -ivh jd…...
乐鑫ESP-Mesh-Lite方案,启明云端乐鑫代理商,创新组网拓展智能应用边界
在当今智能化浪潮的背景下,智能家居、智能农业、能源管理等领域对设备组网的需求日益增长。然而,传统的Wi-Fi组网方式常常受限于设备数量、路由器位置以及网络覆盖范围等因素,难以满足复杂场景下的多样化需求。 一方面,需要支持更…...
ISIS【路由协议讲解】-通俗易懂!
IS-IS的背景 IS-IS最初是国际标准化组织ISO为它的无连接网络协议CLNP(ConnectionLess Network Protocol)设计的一种动态路由协议。随着TCP/IP协议的流行,为了提供对IP路由的支持,IETF在相关标准中对IS-IS进行了扩充和修改…...
