目标

  • 理解面向对象
  • 类和对象
  • 添加和获取对象属性
  • 魔法方法

一. 理解面向对象

面向对象是一种抽象化的编程思想,很多编程语言中都有的一种思想。

例如:洗衣服

思考:几种途径可以完成洗衣服?

答: 手洗 和 机洗。

手洗:找盆 - 放水 - 加洗衣粉 - 浸泡 - 搓洗 - 拧干水 - 倒水 - 漂洗N次 - 拧干 - 晾晒。

机洗:打开洗衣机 - 放衣服 - 加洗衣粉 - 按下开始按钮 - 晾晒。

思考:对比两种洗衣服途径,同学们发现了什么?

答:机洗更简单

思考:机洗,只需要找到一台洗衣机,加入简单操作就可以完成洗衣服的工作,而不需要关心洗衣机内部发生了什么事情。

面向对象编程相当于洗衣机制造商作用。

总结:面向对象就是将编程当成是一个事物,对外界来说,事物是直接使用的,不用去管他内部的情况。而编程就是设置事物能够做什么事。

二. 类和对象

思考:洗衣机洗衣服描述过程中,洗衣机其实就是一个事物,即对象,洗衣机对象哪来的呢?

答:洗衣机是由工厂工人制作出来。

思考:工厂工人怎么制作出的洗衣机?

答:工人根据设计师设计的功能图纸制作洗衣机。

总结:图纸 → 洗衣机 → 洗衣服。

在面向对象编程过程中,有两个重要组成部分:对象

==类和对象的关系:用类去创建一个对象。==也称,用类实例化一个对象

2.1 理解类和对象

2.1.1 类

是对一系列具有相同特征行为的事物的统称,是一个抽象的概念,不是真实存在的事物。

  • 特征即是属性(变量)

  • 行为即是方法(函数)

比如是制造洗衣机时要用到的图纸,也就是说类是用来创建对象。洗衣机功能类似程序中的函数。

image-20190222154356953

2.1.2 对象

对象是类创建出来的真实存在的事物,例如:洗衣机。

注意:开发中,先有类,再有对象。

image-20190222154727379

2.2 面向对象实现方法

2.2.1 定义类

Python2中类分为:经典类 和 新式类

  • 语法

class 类名():  # 小括号是放要继承的类的类名
代码
......

注意:类名要满足标识符命名规则,同时遵循大驼峰命名习惯

  • 示例

class Washer():
def wash(self):
print('我会洗衣服')
  • 拓展:经典类

不由任意内置类型派生出的类,称之为经典类

class 类名:
代码
......

2.2.2 创建对象

对象又名实例。

  • 语法

对象名 = 类名() # 对象即实例
  • 示例

# 创建对象
haier1 = Washer()

# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)

# haier对象调用实例方法/对象方法
haier1.wash()

注意:创建对象的过程也叫实例化对象。

示例:

# 需求:洗衣机,功能:能洗衣服;
# 1 定义洗衣机类
"""
class 类名():
代码
"""
class Washer():
def wash(self):
print('洗衣开始')
def water(self):
print('进水')
def dry(self):
print('干燥')

# 2 创建对象
# 对象名 = 类名()
haier = Washer()

# 3.验证成果
# 打印Haier对象
print(haier)
print(type(haier))

#使用wash功能、dry功能--实例方法/对象方法-- 对象名.wash()
haier.wash()
haier.water()

2.2.3 self

self指的是调用该函数的对象。

self 在 Python 中扮演了一个非常重要的角色,它主要有以下几个作用:

  1. 引用实例对象:在类的方法中,self 作为第一个参数传递,用于引用调用该方法的实例对象自身。通过 self,我们可以访问属性(变量)和其它方法。

  2. 作实例对象的属性和方法。

  3. 区分实例变量和局部变量:在类的方法中,使用 self 来引用实例对象的属性,可以将实例变量与同名的局部变量进行区分。这样可以避免命名冲突和误用。

  4. 创建新的实例变量:通过 self 可以在类的方法中创建新的实例变量。在方法内部,可以使用 self.变量名 = 值 的方式来给实例对象添加新的属性(例如,在__init__中添加属性),并且这些属性会随着实例对象一起存在。

  5. 实现方法的调用:通过 self 可以在类的方法中调用其他的方法。由于方法也是类的属性,所以通过 self.方法名() 的方式可以调用同一个类中的其他方法。

总的来说,self 是一个约定俗成的表示当前实例对象的命名习惯,它提供了一种便捷的方式来访问和操作实例对象的属性和方法,在面向对象的编程中起到了至关重要的作用。

习惯命名为self,也可命名其它名称。

示例:

# 1. 定义类
class Washer():
def wash(self):
print('我会洗衣服')
# haier1对象:<__main__.Washer object at 0x0000024BA2B34240>
# haier2对象:<__main__.Washer object at 0x0000022005857EF0>
print(self)

# 2. 创建对象
haier1 = Washer()
# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)
# haier1对象调用实例方法
haier1.wash()

haier2 = Washer()
# <__main__.Washer object at 0x0000022005857EF0>
print(haier2)
# haier2对象调用实例方法
haier2.wash()

注意:打印对象和self得到的结果是一致的,都是当前对象的内存中存储地址。所以self指的是调用该函数的对象。

在示例中第一个调用self即为对象haier1、第二个调用self即为对象haier2,并将其传入函数内部。

总结:

  • 在Python中,self 是一个约定俗成的命名方式,用于指代类的实例对象(也可以使用其他名称,但习惯上大家都使用 self)。它在类的方法中作为第一个参数传递,用于引用调用该方法的实例对象自身。总之,self 在类的方法中用于表示当前的实例对象,通过它可以访问和操作实例对象的属性和方法。

  • 一个类可以创建多个对象。

  • 多个对象都调用函数时,self地址不相同。因为self表示的是调用该函数的对象。

三. 添加和获取对象属性

属性即是特征,比如:洗衣机的宽度、高度、重量…

对象属性既可以在类外面添加和获取,也能在类里面添加和获取。

3.1 类外面添加对象属性

  • 语法

对象名.属性名 = 值
  • 示例

haier1.width = 500
haier1.height = 800

3.2 类外面获取对象属性

  • 语法

对象名.属性名
  • 示例

print(f'haier1洗衣机的宽度是{haier1.width}')
print(f'haier1洗衣机的高度是{haier1.height}')

示例:在类外面添加对象属性和获取对象属性

class Washer():
def wash(self):
print('洗衣服')


haier1 = Washer()

# 类外面添加对象属性
haier1.width = 400
haier1.height = 100

# 类外面获取对象属性
print(f'洗衣机宽度{haier1.width}')
print(f'洗衣机高度{haier1.height}')

3.3 类里面添加对象属性

  • 语法

self.属性名 = 值
  • 示例

def __init__(self, width, height):
# 类里面添加实例属性
self.width = width
self.height = height

3.4 类里面获取对象属性

  • 语法

self.属性名
  • 示例

class Washer():
def wash(self):
print('洗衣服')
#获取实例属性
def print_info(self):
# self.属性名
print(f'haier1洗衣机的宽度{self.width}')
print(self.width)
print(f'haier1洗衣机的高度{self.height}')
print(self.height)

haier1 = Washer()

# 类外面添加对象属性
haier1.width = 400
haier1.height = 500

# 对象调用实例方法
haier1.print_info()

四. 魔法方法

在Python中,__xx__()的函数叫做魔法方法,指的是具有特殊功能的函数

4.1 __init__()

4.1.1 体验__init__()

思考:洗衣机的宽度高度是与生俱来的属性,可不可以在生产过程中就赋予这些属性呢?

答:理应如此。

__init__()方法的作用:初始化对象。

__init__()用来设置类自带的属性,设置对象的初始化属性

class Washer():    
# 定义初始化功能的函数
def __init__(self):
# 类里面添加实例属性
self.width = 500
self.height = 800

def print_info(self):
# 类里面调用实例属性
print(f'洗衣机的宽度是{self.width}, 高度是{self.height}')

haier1 = Washer()
haier1.print_info()

注意:

  • __init__()方法,在创建一个对象时默认被调用,不需要手动调用。
  • __init__(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。

4.1.2 带参数的__init__()

思考:一个类可以创建多个对象,如何对不同的对象设置不同的初始化属性呢?

答:传参数。带参数的__init__()方法需要在创建实例对象时传入实参。

# 1.定义类: 带参数的init:宽度和高度;  实例方法: 调用实例属性
class Washer():
def __init__(self, width, height):
# 类里面添加实例属性
self.width = width
self.height = height
def print_info(self):
# 类里面调用实例属性
print(f'宽度{self.width}')
print(f'高度{self.height}')

# 2. 创建对象,创建多个对象且属性值不同; 调用实例方法
haiher1 = Washer(100, 200) # 传入实参到init中形参
haiher1.print_info()

# haiher2 = Washer()
# haiher2.print_info()

haiher3 = Washer(300, 700) # 传入实参到init中形参
haiher3.print_info()

4.2 __str__()

当使用print输出对象的时候,默认打印对象的内存地址

如果类定义了__str__方法,那么就会打印从在这个方法中 return 的数据。

class Washer():
def __init__(self, width, height):
# 类里面添加实例属性
self.width = width
self.height = height

def __str__(self):
return '这是海尔洗衣机的说明书'

haier1 = Washer(10, 20)
# 这是海尔洗衣机的说明书
print(haier1)

4.3 __del__()

当删除对象时,python解释器也会默认调用__del__()方法。

class Washer():
def __init__(self, width, height):
self.width = width
self.height = height

def __del__(self):
print(f'{self}对象已经被删除')


haier1 = Washer(10, 20)

# <__main__.Washer object at 0x0000026118223278>对象已经被删除
del haier1

五. 综合应用

5.1 烤地瓜

5.1.1 需求

需求主线:

  1. 被烤的时间和对应的地瓜状态:

    0-3分钟:生的

    3-5分钟:半生不熟

    5-8分钟:熟的

    超过8分钟:烤糊了

  2. 添加的调料:

    用户可以按自己的意愿添加调料

5.1.2 步骤分析

需求涉及一个事物: 地瓜,故案例涉及一个类:地瓜类。

5.1.2.1 定义类

  • 地瓜的属性

    • 被烤的时间
    • 地瓜的状态
    • 添加的调料
  • 地瓜的方法

    • 被烤
      • 用户根据意愿设定每次烤地瓜的时间
      • 判断地瓜被烤的总时间是在哪个区间,修改地瓜状态
    • 添加调料
      • 用户根据意愿设定添加的调料
      • 将用户添加的调料存储
  • 显示对象信息

5.1.2.2 创建对象,调用相关实例方法

5.1.3 代码实现

5.1.3.1 定义类

  • 地瓜属性

    • 定义地瓜初始化属性,后期根据程序推进更新实例属性
class SweetPotato():
def __init__(self):
# 被烤的时间
self.cook_time = 0
# 地瓜的状态
self.cook_static = '生的'
# 调料列表
self.condiments = []

5.1.3.2 定义烤地瓜方法

class SweetPotato():
......

def cook(self, time):
"""烤地瓜的方法"""
self.cook_time += time
if 0 <= self.cook_time < 3:
self.cook_static = '生的'
elif 3 <= self.cook_time < 5:
self.cook_static = '半生不熟'
elif 5 <= self.cook_time < 8:
self.cook_static = '熟了'
elif self.cook_time >= 8:
self.cook_static = '烤糊了'

5.1.3.3 书写str魔法方法,用于输出对象状态

return f'这个地瓜烤了{self.cook_time}分钟, 状态是{self.cook_static}

class SweetPotato():
......

def __str__(self):
return f'这个地瓜烤了{self.cook_time}分钟, 状态是{self.cook_static}'

5.1.3.4 创建对象,测试实例属性和实例方法

digua1 = SweetPotato()
print(digua1)
digua1.cook(2)
print(digua1)

5.1.3.5 定义添加调料方法,并调用该实例方法

class SweetPotato():
......

def add_condiments(self, condiment):
"""添加调料"""
self.condiments.append(condiment)
def __str__(self):
return f'这个地瓜烤了{self.cook_time}分钟, 状态是{self.cook_static}, 添加的调料有{self.condiments}'


digua1 = SweetPotato()
print(digua1)

digua1.cook(2)
digua1.add_condiments('酱油')
print(digua1)

digua1.cook(2)
digua1.add_condiments('辣椒面儿')
print(digua1)

digua1.cook(2)
print(digua1)

digua1.cook(2)
print(digua1)

5.1.4 代码总览

# 定义类
class SweetPotato():
def __init__(self):
# 被烤的时间
self.cook_time = 0
# 地瓜的状态
self.cook_static = '生的'
# 调料列表
self.condiments = []

def cook(self, time):
"""烤地瓜的方法"""
self.cook_time += time
if 0 <= self.cook_time < 3:
self.cook_static = '生的'
elif 3 <= self.cook_time < 5:
self.cook_static = '半生不熟'
elif 5 <= self.cook_time < 8:
self.cook_static = '熟了'
elif self.cook_time >= 8:
self.cook_static = '烤糊了'

def add_condiments(self, condiment):
"""添加调料"""
self.condiments.append(condiment)

def __str__(self):
return f'这个地瓜烤了{self.cook_time}分钟, 状态是{self.cook_static}, 添加的调料有{self.condiments}'


digua1 = SweetPotato()
print(digua1)

digua1.cook(2)
digua1.add_condiments('酱油')
print(digua1)

digua1.cook(2)
digua1.add_condiments('辣椒面儿')
print(digua1)

digua1.cook(2)
print(digua1)

digua1.cook(2)
print(digua1)

自己编:

# 1.定义类:初始化属性,被烤时间和添加调料方法,显示对象信息的str
class Sweet_Potato():
def __init__(self):
# 添加初始化属性
# 被烤时间
self.cook_time = 0
# 地瓜的状态
self.cook_status = '生的'
# 调料列表
self.condiments = [] # 空列表,为了用户可以多种意愿追加调料
def cook(self, time):
"""烤地瓜的方法"""
# 计算地瓜整体被烤的时间
self.cook_time += time
# 用整体烤过的时间判断地瓜状态
if 0 <= self.cook_time < 3:
self.cook_status = '生的'
elif 3 <= self.cook_time < 5:
self.cook_status = '半生不熟'
elif 5 <= self.cook_time < 8:
self.cook_status = '熟的'
elif 8 <= self.cook_time:
self.cook_status = '烤糊了'

def __str__(self):
return f'设置的烤地瓜时间{self.cook_time},状态是{self.cook_status},调料有{self.condiments}'

def add_condiments(self, condiment):
"""添加调料"""
# 用户意愿的调料追加到调料列表
# self.condiments = [] 初始时为空列表
self.condiments.append(condiment)

# 创建对象并调用对应的实例方法
kaoji = Sweet_Potato()
print(kaoji)

kaoji.cook(2)
kaoji.add_condiments('孜然')
print(kaoji)

kaoji.cook(1)
kaoji.add_condiments('麻辣')
print(kaoji)

kaoji.cook(3)
kaoji.add_condiments('甜面酱')
print(kaoji)

5.2 搬家具

5.2.1 需求

将小于房子剩余面积的家具摆放到房子中

5.2.2 步骤分析

需求涉及两个事物:房子 和 家具,故被案例涉及两个类:房子类 和 家具类。

5.2.2.1 定义类

  • 房子类

    • 实例属性
      • 房子地理位置
      • 房子占地面积
      • 房子剩余面积
      • 房子内家具列表
    • 实例方法
      • 容纳家具
    • 显示房屋信息
  • 家具类

    • 家具名称
    • 家具占地面积

5.2.2.2 创建对象并调用相关方法

5.2.3 代码实现

5.2.3.1 定义类

  • 家具类

class Furniture():
def __init__(self, name, area):
# 家具名字
self.name = name
# 家具占地面积
self.area = area
  • 房子类

class Home():
def __init__(self, address, area):
# 地理位置
self.address = address
# 房屋面积
self.area = area
# 剩余面积
self.free_area = area
# 家具列表
self.furniture = []

def __str__(self):
return f'房子坐落于{self.address}, 占地面积{self.area}, 剩余面积{self.free_area}, 家具有{self.furniture}'

def add_furniture(self, item):
"""容纳家具"""
if self.free_area >= item.area:
self.furniture.append(item.name)
# 家具搬入后,房屋剩余面积 = 之前剩余面积 - 该家具面积
self.free_area -= item.area
else:
print('家具太大,剩余面积不足,无法容纳')

5.2.3.2 创建对象并调用实例属性和方法

bed = Furniture('双人床', 6)
jia1 = Home('北京', 1200)
print(jia1)

jia1.add_furniture(bed) # 将通过Furniture类创建的实例对象bed传入到add_furniture函数中去,此时形参自带Furniture属性和方法,通过item.属性名调用属性。
print(jia1)

sofa = Furniture('沙发', 10)
jia1.add_furniture(sofa)
print(jia1)

ball = Furniture('篮球场', 1500)
jia1.add_furniture(ball)
print(jia1)

5.2.4 代码总览

# 1,定义类-家具类
class Furniture():
# 初始化属性
def __init__(self, name, area):
# 家具名字
self.name = name
# 家具占地面积
self.area = area

# 1.定义类——房子类
class Home():
# 初始化属性
def __init__(self, address, area):
# 房子地理位置
self.address = address
# 房屋面积
self.area = area
# 剩余面积
self.free_area = area
# 家具列表
self.furniture_list = []

def __str__(self):
return f'房子地理位置{self.address},房屋面积{self.area},剩余面积{self.free_area},家具列表{self.furniture_list}'

def add_furniture(self, item):
"""容纳家具"""
# 如果 家具占地面积 <= 房子剩余面积; 可以搬入(家具列表添加家具名字数据 并 更新房子剩余面积)
if self.free_area >= item.area: # item.area调用了家具类中self.area属性
self.furniture_list.append(item.name) # item.name调用了家具类中self.name属性
# 家具搬入后,房屋剩余面积 = 之前剩余面积 - 该家具面积
self.free_area -= item.area
else:
print('家具太大,剩余面积不足,无法容纳')

bed = Furniture('双人床', 21)
set_furniture = Home('北京', 1000)
# print(set_furniture)
set_furniture.add_furniture(bed)
print(set_furniture)


sofa = Furniture('沙发', 211)
set_furniture.add_furniture(sofa)
print(set_furniture)

Tv = Furniture('电视', 321)
set_furniture.add_furniture(Tv)
print(set_furniture)

**问题1:**这段代码中为啥在房子类中函数add_furniture(self, item)可以这样调用item.area和item.name

答:在房子类中的 add_furniture 方法中可以调用 item.areaitem.name是因为在调用 add_furniture 方法时,传入的参数 item 是一个家具对象,该对象是由 Furniture 类创建的实例。

**在 Furniture 类中的 __init__ 方法中,通过 self.name = nameself.area = area 将参数 namearea 赋值给了家具对象的属性 namearea。**因此,家具对象 item 具有 namearea 这两个属性。

因此,在 add_furniture 方法内部,我们可以直接通过 item.areaitem.name 来访问家具对象的属性值,以便完成后续的判断和操作。

总结起来,通过将家具对象作为参数传递给方法,在方法内部就可以访问和操作家具对象的属性。这是面向对象编程的一种常见用法,在方法中操作传入的对象的属性,以实现特定的功能逻辑。

六. 总结

  • 面向对象重要组成部分

      • 创建类
    class 类名():
    代码
    • 对象

    对象名 = 类名()
  • 添加对象属性

    • 类外面
    对象名.属性名 = 值
    • 类里面

    self.属性名 = 值
  • 获取对象属性

    • 类外面
    对象名.属性名
    • 类里面

    self.属性名
  • 魔法方法

    • __init__(): 初始化
    • __str__():输出对象信息
    • __del__():删除对象时调用