当前位置: 首页 > news >正文

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个类属性, nameage,它被所有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个方法, introducestudy. 用于定义对象的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
  1. 在实例方法中, 第一个参数通常是 self, 表示对象自身. 定义实例方法时, 必须将 self 作为方法的第一个参数.通过 self, 方法可以访问对象的属性和其他方法.
  2. 在创建类的实例(对象)时, Python会自动传递 self 参数给实例方法, 不需要手动传入.当调用对象的方法时, 无需显式传递 self, Python会自动将对象本身传递给方法.
  3. 在实例方法内部, 可以通过 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")

实例属性和类属性总结

  1. 定义位置:

    • 实例属性:实例属性是定义在类的方法中(通常是在构造函数 __init__ 中)通过 self 关键字定义的, 每个实例(对象)都有自己的一份实例属性.
    • 类属性:类属性是定义在类的方法之外的属性, 直接在类的内部定义的, 属于整个类, 所有实例共享同一份类属性.
  2. 存储位置:

    • 实例属性:每个实例(对象)都有自己独立的实例属性, 存储在对象中.

    • 类属性:类属性属于整个类, 存储在类中.

  3. 值的独立性:

    • 实例属性:不同实例的同名实例属性是相互独立的, 一个实例的实例属性修改不会影响其他实例.
    • 类属性:所有实例共享同一份类属性, 但是通过一个实例修改类属性时, 这个修改不会影响其他实例的类属性.类属性是属于类的, 而不是属于实例的, 因此每个实例都拥有独立的类属性副本, 互不影响.
  4. 访问方式:

    • 实例属性:通过对象访问, 使用表达式 对象名.属性名.

    • 类属性:可以通过类名访问, 也可以通过对象访问.使用表达式 类名.属性名对象名.属性名.

5. __str__

__str__是Python中的特殊方法(魔法方法), 用于定义类的实例对象的字符串表示.

当我们使用print函数或str()函数打印一个类的实例对象时, 实际上是调用了该对象的__str__方法来获取其字符串表示.

如果在类中定义了__str__方法, 那么当我们打印该类的实例时, 会输出__str__方法返回的字符串.这对于自定义类的字符串表示非常有用, 可以让我们以更加直观和可读的方式展示对象的内容.

主要用途包括:

  1. 打印:当你使用print函数打印一个对象时, 实际上是调用该对象的__str__方法来获取其字符串表示, 从而以更直观和易读的方式显示对象的信息.
  2. 字符串转换:当你使用str()函数来将一个对象转换为字符串时, 同样会调用该对象的__str__方法, 以获得其字符串表示.
  3. 字符串格式化:在字符串格式化时, 如果包含了对象, 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天:初识面向对象

你好&#xff0c;我是安然无虞。 文章目录 面向对象编程1. 什么是面向对象2. 类(class)3. 类的实例关于self 4. 对象的初始化5. __str__6. 类之间的关系继承关系组合关系 7. 补充练习 面向对象编程 1. 什么是面向对象 面向对象编程是一种编程思想,它将现实世界的概念和关系映…...

困于环中的机器人

************* c topic: 1041. 困于环中的机器人 - 力扣&#xff08;LeetCode&#xff09; ************* Inspect the topic first. And it looks really familiar with another robot topic. 657. 机器人能否返回原点 - 力扣&#xff08;LeetCode&#xff09;https://lee…...

阿里 FunASR 开源中文语音识别大模型应用示例(准确率比faster-whisper高)

文章目录 Github官网简介模型安装非流式应用示例流式应用示例 Github https://github.com/modelscope/FunASR 官网 https://www.funasr.com/#/ 简介 FunASR是一个基础语音识别工具包&#xff0c;提供多种功能&#xff0c;包括语音识别&#xff08;ASR&#xff09;、语音端…...

spring boot前后端开发上传文件时报413(Request Entity Too Large)错误的可能原因及解决方案

可能原因及解决方案 1. Spring Boot默认文件大小限制 原因&#xff1a;Spring Boot默认单文件最大为1MB&#xff0c;总请求体限制为10MB。解决方案&#xff1a; 在application.properties中配置&#xff1a;spring.servlet.multipart.max-file-size10MB # 单文件最大 spring…...

Transformer:破局山地暴雨预测的「地形诅咒」--AI智能体开发与大语言模型的本地化部署、优化技术

极端降雨预测的技术痛点与边缘破局 1. 传统预警系统的三重瓶颈‌ ‌延迟致命‌&#xff1a;WRF模式在1km分辨率下3小时预报耗时>45分钟&#xff0c;错过山洪黄金响应期 ‌地形干扰大‌&#xff1a;复杂地形区&#xff08;如横断山脉&#xff09;降水预测误差超50% ‌数据…...

游戏引擎学习第187天

看起来观众解决了上次的bug 昨天遇到了一个相对困难的bug&#xff0c;可以说它相当棘手。刚开始的时候&#xff0c;没有立刻想到什么合适的解决办法&#xff0c;所以今天得从头开始&#xff0c;逐步验证之前的假设&#xff0c;收集足够的信息&#xff0c;逐一排查可能的原因&a…...

05-02-自考数据结构(20331)- 动态查找-知识点

自考数据结构动态查找算法主要讲二叉树和平衡二叉树,但是感觉到了,就又续接了一部分,所以这篇备考的小伙伴着重看前两种就可以了。 知识拓扑 知识点介绍 二叉排序树(BST) 定义 二叉排序树(Binary Search Tree)又称二叉查找树,它或者是一棵空树,或者是具有下列性质的二…...

PyQt6实例_批量下载pdf工具_使用pyinstaller与installForge打包成exe文件

目录 前置&#xff1a; 步骤&#xff1a; step one 准备好已开发完毕的项目代码 step two 安装pyinstaller step three 执行pyinstaller pdfdownload.py&#xff0c;获取初始.spec文件 step four 修改.spec文件&#xff0c;将data文件夹加入到打包程序中 step five 增加…...

NVR接入录像回放平台EasyCVR视频融合平台城市/乡镇污水处理厂解决方案

一、方案背景 随着经济的快速发展和城市化的加快&#xff0c;城市污水排放量急剧增加&#xff0c;给城市环境和粮食安全带来了威胁。因此&#xff0c;污水处理厂的建设和高效运营成为对城市环境保护的重要任务。 目前&#xff0c;国内许多城市虽已建成污水处理系统&#xff0…...

Vue2 vs Vue3 生命周期全面对比:created 的进化与革新!!!

&#x1f3af; Vue2 vs Vue3 生命周期全面对比&#xff1a;created 的进化与革新 &#x1f525; 核心差异全景图 一、钩子函数命名与定位变化 1. 命名规范革新 Vue2 钩子Vue3 钩子 (Options API)Vue3 Composition APIbeforeCreate❌ 无setup() 替代created✅ 保留setup() 替代…...

Ubuntu 22.04安装MongoDB:GLM4模型对话数据收集与微调教程

在Ubuntu 22.04安装MongoDB Community Edition的教程请点击下方链接进行参考&#xff1a; 点击这里获取MongoDB Community Edition安装教程 今天将为大家带来如何微调GLM4模型并连接数据库进行对话的教程。快跟着小编一起试试吧~ 1. 大模型 ChatGLM4 微调步骤 1.1 从 github…...

并查集(Union-Find Set)课程笔记

目录 1. 并查集原理 2. 并查集的实现 3. 并查集应用 应用 1&#xff1a;省份数量问题 应用 2&#xff1a;等式方程的可满足性 1. 并查集原理 并查集用于处理需要将不同元素划分成若干不相交集合的问题。最开始时&#xff0c;每个元素都是单独的一个集合&#xff0c;随后根…...

算法刷题记录——LeetCode篇(1.4) [第31~40题](持续更新)

更新时间&#xff1a;2025-03-29 算法题解目录汇总&#xff1a;算法刷题记录——题解目录汇总技术博客总目录&#xff1a;计算机技术系列博客——目录页 优先整理热门100及面试150&#xff0c;不定期持续更新&#xff0c;欢迎关注&#xff01; 32. 最长有效括号 给你一个只包…...

【区块链安全 | 第十四篇】类型之值类型(一)

文章目录 值类型布尔值整数运算符取模运算指数运算 定点数地址&#xff08;Address&#xff09;类型转换地址成员balance 和 transfersendcall&#xff0c;delegatecall 和 staticcallcode 和 codehash 合约类型&#xff08;Contract Types&#xff09;固定大小字节数组&#x…...

一款超级好用且开源免费的数据可视化工具——Superset

认识Superset 数字经济、数字化转型、大数据等等依旧是如今火热的领域&#xff0c;数据工作有一个重要的环节就是数据可视化。 看得见的数据才更有价值&#xff01; 现如今依旧有多数企业号称有多少多少数据&#xff0c;然而如果这些数据只是呆在冷冰冰的数据库或文件内则毫无…...

android gradle一直编译不下来,可能是打开了gradle离线模式

gradle离线模式 当然&#xff0c;如果本地已经将gradle&#xff0c;lib都下载下来了&#xff0c;也可以打开这个离线模式&#xff0c;不然重启AS的时候可能会重新走一次下载流程...

(C语言)学生信息表(学生管理系统)(基于通讯录改版)(正式版)(C语言项目)

1.首先是头文件&#xff1a; //student.h //头文件//防止头文件被重复包含#pragma once//宏定义符号常量&#xff0c;方便维护和修改 #define ID_MAX 20 #define NAME_MAX 20 #define AGE_MAX 5 #define SEX_MAX 5 #define CLA_MAX 20 //定义初始最大容量 #define MAX 1//定义结…...

【Linux】Linux 系统启动流程详解

1. BIOS/UEFI 阶段 硬件自检&#xff08;POST&#xff09; BIOS/UEFI 执行硬件检查&#xff08;内存、CPU、外设等&#xff09;。若硬件异常&#xff0c;通过蜂鸣码或屏幕提示错误。 选择启动设备 按配置顺序&#xff08;硬盘、U盘、网络等&#xff09;寻找可引导设备。BIOS&a…...

Jetson 设备卸载 OpenCV 4.5.4 并编译安装 OpenCV 4.2.0

‌一、卸载 OpenCV 4.5.4‌ 清除已安装的 OpenCV 库‌ sudo apt-get purge libopencv* python3-opencv # 卸载所有APT安装的OpenCV包‌:ml-citation{ref"1,3" data"citationList"}sudo apt autoremove # 清理残留依赖‌:ml-citation{ref"1,4"…...

【计算机网络】OSI七层模型完全指南:从比特流到应用交互的逐层拆解

OSI模型 导读一、概念二、模型层次结构2.1 物理层&#xff08;Physical Layer&#xff09;2.2 数据链路层&#xff08;Data Link Layer&#xff09;​2.3 ​网络层&#xff08;Network Layer&#xff09;​2.4 ​传输层&#xff08;Transport Layer&#xff09;​2.5 ​会话层&…...

渗透测试:登录页面的测试-弱口令思路和实战

渗透测试&#xff1a;登录页面的测试思路和实战 渗透测试&#xff08;Penetration Testing&#xff09;&#xff0c;也称为“渗透性测试”&#xff0c;是一种评估计算机系统、网络或Web应用安全性的一种方法。它通过模拟真实世界中的攻击手段和策略&#xff0c;来检测目标系统…...

Android BottomNavigationView 完全自定义指南:图标、文字颜色与选中状态

1. 核心功能概述 通过 Material Design 的 BottomNavigationView&#xff0c;你可以轻松实现以下自定义&#xff1a; ✅ 动态切换选中/默认图标 ✅ 自定义选中与默认文字颜色 ✅ 控制文字显示模式&#xff08;始终显示/仅选中显示/自动隐藏&#xff09; ✅ 添加动画和高级样…...

提示词工程

参考网站&#xff1a;提示工程指南 – Nextra 声明&#xff1a;我现在也才刚刚开始学习 人工智能&#xff0c;我会着重于 agent 的学习&#xff0c;如果有不对的地方请大家及时指出。 模型设置 前言 在向大模型发送请求时&#xff0c;常常能看到以下参数&#xff1a; {&qu…...

分页查询原理与优化方案完全指南

分页查询原理与优化方案完全指南 一、分页查询基础原理 1.1 传统分页实现方式 分页查询的核心目的是将大数据集分割成多个小块进行展示,最常见的实现方式是使用LIMIT-OFFSET语法: -- 基础分页查询 SELECT * FROM table_name ORDER BY id LIMIT page_size OFFSET (page_n…...

嵌入式软件设计规范框架(MISRA-C 2012增强版)

以下是一份基于MISRA-C的嵌入式软件设计规范&#xff08;完整技术文档框架&#xff09;&#xff0c;包含编码规范、安全设计原则和工程实践要求&#xff1a; 嵌入式软件设计规范&#xff08;MISRA-C 2012增强版&#xff09; 一、编码基础规范 1.1 文件组织 头文件保护 /* 示…...

课程6. 决策树

课程6. 决策树 决策树直觉模型结构几何解释决策树的构建ID3算法信息内容标准使用决策树处理差距推广到回归问题分支标准与经典损失函数的关系 过度拟合和欠拟合欠拟合过拟合 优点和缺点案例随机生成数据集分类IRIS 数据集解决回归问题的一个简短例子 决策树 今天我们继续探索一…...

【UE5.3.2】初学1:适合初学者的入门路线图和建议

3D人物的动作制作 大神分析:3D人物的动作制作通常可以分为以下几个步骤: 角色绑定(Rigging):将3D人物模型绑定到一个骨骼结构上,使得模型能够进行动画控制。 动画制作(Animation):通过控制骨骼结构,制作出人物的各种动作,例如走路、跳跃、打斗等。 动画编辑(Ani…...

OpenCV 图形API(4)内核 API

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 G-API 背后的核心理念是可移植性——使用 G-API 构建的流水线必须是可移植的&#xff08;或者至少具备可移植的能力&#xff09;。这意味着&…...

pom.xml与.yml,java配置参数传递

pom.xml与 .yml java配置参数传递 在Java项目中&#xff0c;通过 pom.xml 和 .yml 文件&#xff08;如 application.yml&#xff09;传递变量通常涉及 构建时&#xff08;Maven&#xff09;和 运行时&#xff08;Spring Boot&#xff09;两个阶段的配置。以下是具体的实现方法&…...

LeetCode算法题(Go语言实现)_21

题目 给你一个整数数组 arr&#xff0c;如果每个数的出现次数都是独一无二的&#xff0c;就返回 true&#xff1b;否则返回 false。 一、代码实现 func uniqueOccurrences(arr []int) bool {freq : make(map[int]int)// 统计每个数字的出现次数for _, num : range arr {freq[n…...

Docker部署前后端分离项目

镜像下载 在有网络的电脑下载镜像&#xff08;Windows&#xff09;&#xff1a;依次在CMD命令台执行以下代码 docker pull node:20docker pull openjdk:22-jdkdocker pull mysql:8.0docker pull nginx:1.27 删除镜像代码&#xff1a; docker rmi node:latest 查看镜像列表…...

Linux系统安装MySQL 8.0完整指南(新手友好版)

MySQL作为最流行的开源关系型数据库之一&#xff0c;广泛应用于各种开发和生产环境。本教程将详细介绍在Linux系统上安装MySQL 8.0的全过程&#xff0c;包括卸载旧版本、安装新版本、基础配置和远程连接设置&#xff0c;特别适合Linux新手学习使用。 一、卸载旧版MySQL&#x…...

第二次作业

#创建表&#xff0c;把id设为主键 mysql> create table test02(-> id int primary key, #----主键约束-> name varchar(50)-> ); Query OK, 0 rows affected (0.02 sec) ​ #插入数据测试 mysql> insert into test02 values(1,"成都"); Query OK, 1 r…...

AI大模型下传统 Spring Java工程开发的演进和变化方向

1. 背景和动因 传统Spring开发优势&#xff1a;Spring生态以稳定、模块化、依赖注入&#xff08;DI&#xff09;等特性著称&#xff0c;长期支撑企业级应用开发&#xff0c;具备高扩展性和可维护性。AI大模型崛起&#xff1a;近几年&#xff0c;LLM&#xff08;如GPT-4、LLaMA…...

周学习总结

这周继续学习了Java的知识点&#xff0c;还写了考查递归、递推与贪心的算法题。 算法小结 递归与递推一般是观察观察题干&#xff0c;分析题目的规律&#xff0c;可能还会用到分治算法&#xff0c;推导出一个合理的表达式&#xff0c;再使用函数递归来进行求解。 贪心在求解时…...

‌19.思科路由器:OSPF协议引入直连路由的实验研究

思科路由器:OSPF协议引入直连路由的实验研究 一、实验拓扑二、基本配置2.1、sw1的配置2.2、开启交换机三层功能三、ospf的配置3.1、R1的配置3.2、R2的配置3.3、重启ospf进程四、引入直连路由五、验证结果随着互联网技术的不断发展,路由器作为网络互联的关键设备,其性能与稳定…...

Zcanpro搭配USBCANFD-200U在新能源汽车研发测试中的应用指南(周立功/致远电子)

——国产工具链的崛起与智能汽车测试新范式 引言&#xff1a;新能源汽车测试的国产化突围 随着新能源汽车智能化、网联化程度的提升&#xff0c;研发测试面临三大核心挑战&#xff1a;多协议融合&#xff08;CAN FD/LIN/以太网&#xff09;、高实时性数据交互需求、复杂工况下…...

JSON的基础知识

文章目录 前言json协议的基本格式json 数组类型 的语法规则json协议报文的实例json常见的一些格式错误在gd32中使用cjson库小结 前言 json协议在互联网应用&#xff0c;物联网应用中都会用到。所谓工欲善其事必先利其器&#xff0c;我们需要学习了解json协议的具体格式&#xf…...

week2|机器学习(吴恩达)学习笔记

一、多维特征 1.1、什么是多维特征&#xff1f; 1&#xff09;在我们的原始估计房价的版本中&#xff0c;我们只有一个变量&#xff1a; x x x 来预估 y y y 2&#xff09;但是现在假设你也知道其他的参数变量&#xff0c;那么我们就可以引入多个参数来提高预测 y y y的准确…...

各类神经网络学习:(七)GRU 门控循环单元(上集),详细结构说明

上一篇下一篇LSTM&#xff08;下集&#xff09;GRU&#xff08;下集&#xff09; GRU&#xff08;门控循环单元&#xff09; 它其实是 R N N RNN RNN 和 L S T M LSTM LSTM 的折中版&#xff0c;有关 R N N RNN RNN 和 L S T M LSTM LSTM 请参考往期博客。 实际应用要比 …...

uniapp利用第三方(阿里云)实现双人视频/音频通话功能(附完整的项目代码)

要在UniApp中利用阿里云实现双人视频/音频通话功能,你需要使用阿里云的实时音视频服务(RTC)。以下是一个基本的实现步骤和示例代码。 基本的操作步骤 注册阿里云账号并开通RTC服务: 访问阿里云官网,注册账号并开通RTC服务。 获取AppID和AppKey: 在RTC控制台创建应用,…...

wsl2的centos7安装jdk17、maven

JDK安装 查询系统中的jdk rpm -qa | grep java按照查询的结果&#xff0c;删除对应版本 yum -y remove java-1.7.0-openjdk*检查是否删除 java -version 下载JDK17 JDK17&#xff0c;下载之后存到wsl目录下&#xff08;看你自己&#xff09;然后一键安装 sudo rpm -ivh jd…...

Android 单例模式全解析:从基础实现到最佳实践

单例模式&#xff08;Singleton Pattern&#xff09;是软件开发中常用的设计模式&#xff0c;其核心是确保一个类在全局范围内只有一个实例&#xff0c;并提供全局访问点。在 Android 开发中&#xff0c;单例模式常用于管理全局资源&#xff08;如网络管理器、数据库助手、配置…...

Redis GEO

Redis GEO 引言 Redis GEO是Redis数据库中的一种高级功能&#xff0c;允许用户存储地理位置信息并执行基于地理空间查询的操作。本文将详细介绍Redis GEO的基本概念、使用方法以及在实际应用中的优势。 基本概念 GEO编码 GEO编码是指将地理位置信息&#xff08;如经纬度&a…...

vulnhub-serile靶机通关攻略

下载地址&#xff1a;https://www.vulnhub.com/entry/serial-1,349/ 靶机安装特殊&#xff0c;附带安装参考文章&#xff1a;https://zhuanlan.zhihu.com/p/113887109 扫描IP地址 arp-scan -l扫描端口 nmap -p- 192.168.112.141访问80端口 线索指向cookie cookie是base64编…...

SAP-ABAP:OData 协议深度解析:架构、实践与最佳应用

OData 协议深度解析:架构、实践与最佳应用 一、协议基础与核心特性 协议定义与目标 定位:基于REST的开放数据协议,标准化数据访问接口,由OASIS组织维护,最新版本为OData v4.01。设计哲学:通过统一资源标识符(URI)和HTTP方法抽象数据操作,降低异构系统集成复杂度。核心…...

408 计算机网络 知识点记忆(3)

前言 本文基于王道考研课程与湖科大计算机网络课程教学内容&#xff0c;系统梳理核心知识记忆点和框架&#xff0c;既为个人复习沉淀思考&#xff0c;亦希望能与同行者互助共进。&#xff08;PS&#xff1a;后续将持续迭代优化细节&#xff09; 往期内容 408 计算机网络 知识…...

java学习笔记10——集合框架

枚举类的使用 Collection接口继承树 Map接口继承树 Collection 接口方法 总结&#xff1a; 集合框架概述 1.内存层面需要针对于多个数据进行存储。此时&#xff0c;可以考虑的容器有:数组、集合类2.数组存储多个数据方面的特点:> 数组一旦初始化&#xff0c;其长度就是确定的…...

埃文科技企业AI大模型一体机——昇腾体系+DeepSeek+RAG一站式解决方案

面对企业级市场海量数据资产与复杂业务场景深度耦合的刚需&#xff0c;埃文科技重磅推出基于华为昇腾算力DeepSeek大模型的企业一体机产品&#xff0c;提供DeepSeek多版本大模型一体机选择&#xff0c;为企业提供本地昇腾算力DeepSeek大模型RAG知识库的一体化解决方案&#xff…...

蓝桥杯---BFS解决FloofFill算法1---图像渲染

文章目录 1.算法简介2.题目概述3.算法原理4.代码分析 1.算法简介 这个算法是关于我们的floodfill的相关的问题&#xff0c;这个算法其实从名字就可以看出来&#xff1a;洪水灌溉&#xff0c;其实这个算法的过程就和他的名字非常相似&#xff0c;下面的这个图就生动的展示了这个…...