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()
遇见安然遇见你,不负代码不负卿。
谢谢老铁的时间,咱们下篇再见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/76242.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot中常用内嵌数据库(H2、HSQLDB、Derby)的对比,包含配置示例和关键差异总结

以下是Spring Boot中常用内嵌数据库的对比&#xff0c;包含配置示例和关键差异总结&#xff1a; 一、主流内嵌数据库对比 1. H2 数据库 特点&#xff1a; 支持内存模式&#xff08;速度快&#xff09;和文件模式&#xff08;数据持久化&#xff09;。支持SQL方言&#xff08…

Apache Hive和Snowflake的`CREATE VIEW`语法和功能特性整理的对比表

写一个Apache Hive中CREATE VIEW语句转换为对应Snowflake中CREATE VIEW语句的程序&#xff0c;现在需要一个根据功能的相似性对应的Apache HiveQL和Snowflake SQL的CREATE VIEW语句的表。 以下是基于Apache Hive的CREATE VIEW语法规则构造的所有可能合法语句实例及其功能说明&…

个人博客网站从搭建到上线教程

步骤1:设计个人网站 设计个人博客网站的风格样式,可以在各个模板网站上多浏览浏览,以便有更多设计网站风格样式的经验。 设计个人博客网站的内容,你希望你的网站包含哪些内容如你的个人基本信息介绍、你想分享的项目、你想分享的技术文档等等。 步骤2:选择开发技术栈 因…

PHP回调后门

1.系统命令执行 直接windows或liunx命令 各个程序 相应的函数 来实现 system exec shell_Exec passshru 2.执行代码 eval assert php代码 系统 <?php eval($_POST) <?php assert($_POST) 简单的测试 回调后门函数call_user_func(1,2) 1是回调的函数 2是回调…

Raspberry 树莓派 CM4模块的底板设计注意事项

1&#xff0c; 树莓派CM4底板设计 树莓派CM4模块集成了CPU&#xff0c; 存储器&#xff0c;以太网&#xff0c; 无线模块&#xff0c;电源等等&#xff0c; 大大降低了硬件设计的要求。对我们使用树莓派提供了很好的便利性。 本人近期因为项目的需要设计了一款CM4的底板&#x…

Java后端开发(十八)-- 使用JAXB,将JavaBean转换XML文本

下面是测试时的运行环境: 1.jdk8 2.Maven,可能需要需要的依赖,如下: <dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version></dependency><dependency><gr…

【一起来学kubernetes】30、k8s的java sdk怎么用

Kubernetes Java SDK 是开发者在 Java 应用中与 Kubernetes 集群交互的核心工具&#xff0c;支持资源管理、服务发现、配置操作等功能。 一、主流 Java SDK 对比与选择 官方 client-java 库 特点&#xff1a;由 Kubernetes 社区维护&#xff0c;API 与 Kubernetes 原生对象严格…

PHP开发者2025生存指南

PHP&#xff0c;这个曾经被戏称为“世界上最好的语言”的脚本语言&#xff0c;依旧在网络世界占据着重要的地位。然而&#xff0c;技术发展日新月异&#xff0c;面向2025年&#xff0c;PHP开发者要想保持竞争力甚至实现职业生涯的飞跃&#xff0c;需要不断学习和提升自身技能。…

MySQL与Redis数据一致性保障方案详解

前言 在现代分布式系统中&#xff0c;MySQL和Redis的结合使用非常普遍。MySQL作为关系型数据库负责持久化存储&#xff0c;而Redis则作为高性能缓存层提升系统的响应速度。然而&#xff0c;在这种架构下&#xff0c;如何保证MySQL与Redis之间的数据一致性是一个重要的挑战。本…

MySQL响应慢是否由堵塞或死锁引起?

目录标题 **1. 检查当前运行的查询和进程****2. 查看死锁日志****方法一&#xff1a;通过错误日志****方法二&#xff1a;通过InnoDB状态** **3. 检查锁信息****查看表锁****查看行锁&#xff08;InnoDB&#xff09;** **4. 分析慢查询****开启慢查询日志****分析慢查询** **5.…

【计算机网络】记录一次校园网无法上网的解决方法

问题现象 环境&#xff1a;实训室教室内时间&#xff1a;近期突然出现 &#xff08;推测是学校在施工&#xff0c;部分设备可能出现问题&#xff09;症状&#xff1a; 连接校园网 SWXY-WIFI 后&#xff1a; 连接速度极慢偶发无 IP 分配&#xff08;DHCP 失败&#xff09;即使分…

JavaScript函数式编程思想

1. 相关面试题 1.1. 什么是纯函数&#xff1f; 纯函数是一种函数&#xff0c;其返回值仅由其输入参数决定&#xff0c;不产生任何可观察的副作用&#xff0c;如修改全局对象或外部状态。 纯函数具有以下特性&#xff1a; 1. 确定性&#xff1a;相同的输入永远得到相同的输…

Elasticsearch安全与权限控制指南

在Elasticsearch维护中&#xff0c;安全管理是保障数据合规性和集群稳定性的关键。本文将详细介绍用户与角色管理、索引/字段级权限控制、HTTPS加密通信、审计日志与合规性检查等核心安全实践&#xff0c;希望可以帮助你构建更安全的Elasticsearch环境。 1 用户与角色管理 1.1…

『VUE』快速入门配置环境使用tailwind css 记忆tailwind css常见规则 (详细图文注释)

目录 效果预览快速入门环境配置配置 tailwind.config.js 设置文件添加 Tailwind 的基础样式引入样式到项目检查构建工具配置测试 Tailwind CSS 效果 使用插件tailwind.config.js的最终内容app.vue演示 为什么不需要记忆 Tailwind 的类名&#xff1f;1. 类名直观2. 文档全面3. 工…

StdioIterator

参考这种用法&#xff1a; int a[3]{1,2,3}; copy(a,a3,ostream_iterator<int>(cout," ")); 以及 ostream_iterator 类 | Microsoft Learn 中的函数签名&#xff0c;可以编写出 StdioIterator&#xff0c;同样支持 copy 函数的调用。 #include <stdio.h&…

制作service列表并打印出来

制作service列表并打印出来 在Linux中&#xff0c;服务&#xff08;Service&#xff09;是指常驻在内存中的进程&#xff0c;这些进程通常监听某个端口&#xff0c;等待其他程序的请求。服务也被称为守护进程&#xff08;Daemon&#xff09;&#xff0c;它们提供了系统所需的各…

CKS认证 | Day3 K8s容器运行环境安全加固

一、最小特权原则&#xff08;POLP&#xff09; 1&#xff09;最小特权原则 (Principle of least privilege&#xff0c;POLP) &#xff1a; 是一种信息安全概念&#xff0c;即为用户提供执行其工作职责所需的最 小权限等级或许可。 最小特权原则被广泛认为是网络安全的最佳实…

Linux wifi 驱动移植适配流程详解

基础内容概要 将tplink wn725n 无线网卡驱动移植到ubuntu将tplink wn725n 无线网卡驱动移植到Linux开发板&#xff08;交叉编译&#xff09;将tplink wn725n 无线网卡驱动移植到Linux开发板&#xff0c;在开发板中编译 为什么还要包涵交叉编译&#xff1f; 目标设备是ARM架构…

Day14 动态规划(3)

一.746. 使用最小花费爬楼梯 FS记忆化搜索优化: const int N 1010;class Solution { public:int mem[N];int dfs(vector<int>& cost, int x){if(mem[x]) return mem[x];int sum 0;if(x 0 || x 1) return 0;else{sum min(dfs(cost, x - 1) cost[x - 1], dfs(c…

解锁AI潜能:模型上下文协议(MCP)的革新与应用

解锁AI潜能:模型上下文协议(MCP)的革新与应用 在人工智能发展的当下,大语言模型(LLM)正逐步渗透到各个领域。从智能客服快速响应客户咨询,到智能编程助手协助开发者高效编写代码,LLM展现出强大的能力。然而,随着应用的深入会面临一个问题:模型与数据之间的连接困境。…