类与对象
目标
- 理解面向对象
- 类和对象
- 添加和获取对象属性
- 魔法方法
面向对象编程的概念:
在面向对象编程中,编写表示现实世界中的事物和情景的类,并基于类创建对象。
编写类时,定义一类对象都有的通用行为。基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。
根据类来创建对象被称为实例化,可以使用类的实例。
理解面向对象
面向对象是一种抽象化的编程思想,很多编程语言中都有的一种思想。
例如:洗衣服
思考:几种途径可以完成洗衣服?
答: 手洗 和 机洗。
手洗:找盆 - 放水 - 加洗衣粉 - 浸泡 - 搓洗 - 拧干水 - 倒水 - 漂洗N次 - 拧干 - 晾晒。
机洗:打开洗衣机 - 放衣服 - 加洗衣粉 - 按下开始按钮 - 晾晒。
思考:对比两种洗衣服途径,同学们发现了什么?
答:机洗更简单
思考:机洗,只需要找到一台洗衣机,加入简单操作就可以完成洗衣服的工作,而不需要关心洗衣机内部发生了什么事情。
总结:面向对象就是将编程当成是一个事物,对外界来说,事物是直接使用的,不用去管他内部的情况。而编程就是设置事物能够做什么事。
类和对象
思考:洗衣机洗衣服描述过程中,洗衣机其实就是一个事物,即对象,洗衣机对象哪来的呢?
答:洗衣机是由工厂工人制作出来。
思考:工厂工人怎么制作出的洗衣机?
答:工人根据设计师设计的功能图纸制作洗衣机。
总结:图纸 → 洗衣机 → 洗衣服。
在面向对象编程过程中,有两个重要组成部分:类
和 对象
。
类和对象的关系:用类去创建一个对象。
理解类和对象
类
类是对一系列具有相同特征
和行为
的事物的统称,是一个抽象的概念,不是真实存在的事物。
- 特征即是属性
- 行为即是方法
类比如是制造洗衣机时要用到的图纸,也就是说类是用来创建对象
。
对象
对象是类创建出来的真实存在的事物,例如:洗衣机。
注意:开发中,先有类,再有对象。
面向对象实现方法
定义类
- 语法
class 类名():
代码
......
注意:类名要满足标识符命名规则,同时遵循
大驼峰命名习惯
。
- 体验
class Washer():
def wash(self):
print('我会洗衣服')
创建对象
对象又名实例。
- 语法
对象名 = 类名()
- 体验
# 创建对象
haier1 = Washer()
# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)
# haier对象调用实例方法
haier1.wash()
注意:创建对象的过程也叫实例化对象。
self
self指的是调用该函数的对象。
# 1. 定义类
class Washer():
def wash(self):
print('我会洗衣服')
# <__main__.Washer object at 0x0000024BA2B34240>
print(self)
# 2. 创建对象
haier1 = Washer()
# <__main__.Washer object at 0x0000018B7B224240>
print(haier1)
# haier1对象调用实例方法
haier1.wash()
haier2 = Washer()
# <__main__.Washer object at 0x0000022005857EF0>
print(haier2)
注意:打印对象和self得到的结果是一致的,都是当前对象的内存中存储地址。
添加和获取对象属性
属性即是特征,比如:洗衣机的宽度、高度、重量...
对象属性既可以在类外面添加和获取,也能在类里面添加和获取。
类外面添加对象属性
- 语法
对象名.属性名 = 值
- 体验
haier1.width = 500
haier1.height = 800
类外面获取对象属性
- 语法
对象名.属性名
- 体验
print(f'haier1洗衣机的宽度是{haier1.width}')
print(f'haier1洗衣机的高度是{haier1.height}')
类里面获取对象属性
- 语法
self.属性名
- 体验
# 定义类
class Washer():
def print_info(self):
# 类里面获取实例属性
print(f'haier1洗衣机的宽度是{self.width}')
print(f'haier1洗衣机的高度是{self.height}')
# 创建对象
haier1 = Washer()
# 添加实例属性
haier1.width = 500
haier1.height = 800
haier1.print_info()
魔法方法
在Python中,__xx__()
的函数叫做魔法方法,指的是具有特殊功能的函数。
__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解释器会自动把当前的对象引用传递过去。
带参数的__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}')
haier1 = Washer(10, 20)
haier1.print_info()
haier2 = Washer(30, 40)
haier2.print_info()
__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)
__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
实例讲解
创建和使用类
下面创建一个表示小狗的简单类Dog,它表示的不是特定小狗,而是任何小狗。它们都有名字和年龄;大多数小狗还会蹲下和打滚。由于大多数小狗都具备上述两项信息(名字和年龄)和两种行为(蹲下和打滚),Dog类将包含这些。创建这个类后,使用它来创建表示特定小狗的实例。
创建Dog类
根据Dog类创建的每个实例都将存储名字和年龄。赋予了每条小狗蹲下(sit() )和打滚(roll_over() )的能力:
class Dog(): #1
def __init__(self, name, age): #2
self.name = name #3
self.age = age
def sit(self): #4
print(self.name.title() + " is now sitting.")
def roll_over(self):
print(self.name.title() + " rolled over!")
在#1
处,定义了一个名为Dog的类,类名的首字母必须大写,后面跟圆括号。
类中的函数称为方法。#2
处的方法__init__()
是一个特殊的方法。开头和末尾各有两个下划线,这种约定避免Python默认方法与普通方法发生名称冲突。
将方法__init__()
定义成包含三个形参:self 、name 和age ,self 必不可少,还必须位于其他形参的前面。
Python调用这个__init__()
方法来创建Dog实例时,将自动传入实参self 。每个与类相关联的方法调用都自动传递实参self ,因此不需要传递它。每当根据Dog 类创建实例时,都只需给后两个形参(name 和age )提供值。
在#3
处定义两个变量都有前缀self 。以self为前缀的变量可供类中所有方法使用,还可以通过类的任何实例来访问这些变量。self.name = name
获取存储在形参name 中的值,并将其存储到变量name 中,然后该变量被关联到当前创建的实例。self.age= age
的作用与此类似。像这样可通过实例访问的变量称为属性 。
Dog类还定义了另外两个方法:sit()
和roll_over()
(在#4
处)。
根据类创建实例
创建一个表示特定小狗的实例:
class Dog(): #1
(代码略)
my_dog = Dog('willie', 6) #5
print("My dog's name is " + my_dog.name.title() + ".") #6
print("My dog is " + str(my_dog.age) + " years old.") #7
在#5
处,创建一条名字为'willie' 、年龄为6 的小狗。遇到这行代码时,Python使用实参'willie' 和6调用Dog 类中的方法__init__()
。方法__init__()
创建一个表示特定小狗的示例,并使用提供的值来设置属性name 和age 。方法__init__()
并未显式地包含return语句,但Python自动返回一个表示这条小狗的实例,将这个实例存储在变量my_dog中。这里的命名约定很有用:通常约定首字母大写的名称(如Dog)指的是类,小写的名称(如my_dog)指的是根据类创建的实例。
访问属性
在#6
处,访问my_dog 的属性name的值:
my_dog.name
要访问实例的属性,可使用句点表示法,这种语法演示了Python如何获悉属性的值。在这里,Python先找到实例my_dog ,再查找与这个实例相关联的属性name 。
在Dog 类中引用这个属性时,使用的是self.name
。在#7处,使用同样的方法来获取属性age 的值。
在前面的第1条print 语句中,my_dog.name.title() 将my_dog 的属性name 的值'willie' 改为首字母大写的;
在第2条print 语句中,str(my_dog.age) 将my_dog的属性age 的值6转换为字符串。
运行结果如下:
My dog's name is Willie.
My dog is 6 years old.
调用方法
根据Dog类创建实例后,就可以使用句点表示法来调用Dog 类中定义的任何方法。下面来让小狗蹲下和打滚:
class Dog(): #1
(代码略)
my_dog = Dog('willie', 6) #5
my_dog.sit()
my_dog.roll_over()
要调用方法,可指定实例的名称(这里是my_dog )和要调用的方法,并用句点分隔。遇到代码my_dog.sit()时,Python在类Dog 中查找方法sit() 并运行其代码。
Python以同样的方式解读代码my_dog.roll_over() 。
创建多个实例
创建一个表示特定小狗的实例:
class Dog(): #1
(代码略)
my_dog = Dog('willie', 6) #5
your_dog = Dog('lucy', 3)
print("My dog's name is " + my_dog.name.title() + ".") #6
print("My dog is " + str(my_dog.age) + " years old.") #7
my_dog.sit()
print("\nYour dog's name is " + your_dog.name.title() + ".")
print("Your dog is " + str(your_dog.age) + " years old.")
your_dog.sit()
在这个实例中,创建了两条小狗,它们分别名为Willie和Lucy。每条小狗都是一个独立的实例,有自己的一组属性,能够执行相同的操作:
运行结果如下:
My dog's name is Willie.
My dog is 6 years old.
Willie is now sitting.
Your dog's name is Lucy.
Your dog is 3 years old.
Lucy is now sitting.
综合应用
烤地瓜
需求
需求主线:
被烤的时间和对应的地瓜状态:
0-3分钟:生的
3-5分钟:半生不熟
5-8分钟:熟的
超过8分钟:烤糊了
添加的调料:
用户可以按自己的意愿添加调料
步骤分析
需求涉及一个事物: 地瓜,故案例涉及一个类:地瓜类。
定义类
- 地瓜的属性
- 被烤的时间
- 地瓜的状态
- 添加的调料
地瓜的方法
- 被烤
- 用户根据意愿设定每次烤地瓜的时间
- 判断地瓜被烤的总时间是在哪个区间,修改地瓜状态
- 添加调料
- 用户根据意愿设定添加的调料
- 将用户添加的调料存储
- 被烤
显示对象信息
创建对象,调用相关实例方法
代码实现
定义类
- 地瓜属性
- 定义地瓜初始化属性,后期根据程序推进更新实例属性
class SweetPotato():
def __init__(self):
# 被烤的时间
self.cook_time = 0
# 地瓜的状态
self.cook_static = '生的'
# 调料列表
self.condiments = []
定义烤地瓜方法
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 = '烤糊了'
书写str魔法方法,用于输出对象状态
class SweetPotato():
......
def __str__(self):
return f'这个地瓜烤了{self.cook_time}分钟, 状态是{self.cook_static}'
创建对象,测试实例属性和实例方法
digua1 = SweetPotato()
print(digua1)
digua1.cook(2)
print(digua1)
定义添加调料方法,并调用该实例方法
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)
代码总览
# 定义类
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)
搬家具
需求
将小于房子剩余面积的家具摆放到房子中
步骤分析
需求涉及两个事物:房子 和 家具,故被案例涉及两个类:房子类 和 家具类。
定义类
- 房子类
- 实例属性
- 房子地理位置
- 房子占地面积
- 房子剩余面积
- 房子内家具列表
- 实例方法
- 容纳家具
- 显示房屋信息
- 实例属性
- 家具类
- 家具名称
- 家具占地面积
创建对象并调用相关方法
代码实现
定义类
- 家具类
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('家具太大,剩余面积不足,无法容纳')
创建对象并调用实例属性和方法
bed = Furniture('双人床', 6)
jia1 = Home('北京', 1200)
print(jia1)
jia1.add_furniture(bed)
print(jia1)
sofa = Furniture('沙发', 10)
jia1.add_furniture(sofa)
print(jia1)
ball = Furniture('篮球场', 1500)
jia1.add_furniture(ball)
print(jia1)
模拟考题
考题1 单选题
有Python程序段如下,下列选项错误的是?( )
class Car():
def __init__(self,name,color):
self.name=name
self.color=color
def run(self):
print(self.color +self.name+"is running")
A. 使用class关键字来定义一个Car类,类名的首字母必须要大写
B. 方法__init()__定义了三个参数:self、name和color,其中self参数可省略
C. 语句“self.color=color”获取存储在参数color中的值并存储到self的属性color中
D. Car类还定义了一个方法run()
答案:B
解析:方法__init()__定义了三个形参:self、name和color,其中self参数必不可少,且必须位于所有参数的前面。Python调用init()来创建Car实例时,将自动传入实参self,每个与类相关联的方法调用都能够自动传递实参self。每当根据Car类创建实例时,都只需给后两个形参(name和color)提供值。因此self不能省略。
考题2 单选题
创建了Car类后,用以下代码语句创建了car对象:car=Car()
那么需要调用car对象的drive方法,下列选项正确的是?( )
A. car.drive()
B. Car.drive()
C. Car.drive
D. car.drive
答案:A
解析:根据Car类创建实例(对象)后,就可以使用句点表示法来调用Car类中定义的任何方法。要调用方法,可指定实例的名称(这里就是car)和要调用的方法dive(),并用句点分隔。遇到代码car.drive()时,Python在类Car中查找方法drive()并运行其代码。方法调用格式:实例名.方法名()
考题3 单选题
以下代码的运行结果是?( )
class Num():
def __init__(self,a,b,c):
self.a=a
self.b=b
self.c=c
def run(self):
print(self.a*10)
print(self.b*5)
print(self.c*2)
e = Num('C','BB','AAA')
e.run()
A.
AAAAAAAAAA
BBBBBBBBBB
CCCCCC
B.
AAAAAAAAAA
BBBBB
CC
C.
CCCCCCCCCC
BBBBBBBBBB
AAAAAA
D.
CCCCCCCCCC
BBBBB
AA
答案:C
解析:#创建了Num类的实例e,自动传入实参self,并给后三个形参a,b,c分别提供了三个值'C','BB','AAA',输出时根据语句参数'C'要10次,'BB'要5次,'AAA'要2次,所以选C。
考题4 单选题
有如下程序代码:
class Person(): # ①
def __init__(self, name, age): # ②
self.name = name # ③
self.age = age
xm = Person("小红", "10") # ④
下列说法正确的是?( )
A. ①处Person为类名
B. ②处的self可以省略
C. ③处self.name为方法
D. ④处可以不加参数
答案:A
解析:②处的self不可以省略,③处self.name为属性,④处参数个数为2
考题5 单选题
有如下程序段:
class Person():
def __init__(self, name, age):
self.name = name
self.age = age
def fun(self):
print(self.name,end = ",")
print(self.age)
xm = Person("10", "小红")
xm.fun()
输出的结果是?( )
A. 10 小红
B. 小红 10
C. 10,小红
D. 小红,10
答案:C
解析:按参数顺序传参,Person("10", "小红")先打印10,再打印小红,根据 print(self.name,end = ",")可知中间用逗号分隔。
考题6 单选题
在Python的类定义中,对函数变量的访问形式是?( )
A. <对象>.<变量>
B. <对象>.方法
C. <类名>.<变量>
D. <类名>.方法
答案:A
解析:访问函数的变量时,需要使用点表示法:对象.变量名
考题7 单选题
执行下列程序,输出的结果是?( )
class Money():
name =""
shoe =0
satchel =0
clothes =0
def __init__(self,n,s1,s2,c):
self.name = n
self.shoe = s1
self.satchel = s2
self.clothes =c
def rmb_1(self):
print("{}今天去购物,总共花了{}元。". format(self.name,self.shoe+self.satchel+ self.clothes))
r= Money('李斌',120,50,280)
r.rmb_1()
A. 李斌今天去购物,总共花了280元
B. 今天去购物,总共花了450元
C. 李斌今天去购物,总共花了450元
D. 今天去购物,总共花了280元
答案:C
解析:输出的结果格式为:{}今天去购物,总共花了{}元。故排除BD,根据代码可知,花费为三者之和。故答案选C
考题8 单选题
下列关于类和对象的说法,正确的是?( )
A. 通过def关键字定义类
B. 通过class创建实例
C. 每个对象的数据相同
D. 每个对象拥有相同的方法
答案:D
解析:Python定义类是通过class关键字,通过类名+()实现创建实例,每个对象的数据可能不同,每个对象拥有相同的方法
考题9 单选题
有如下程序段:
class Student():
count = 0
def __init__(self, name):
self.name = name
Student.count += 1
def study(self):
print(f'{self.name}在学习')
student1 = Student("小明")
student2 = Student("小红")
student2.study()
执行代码后,下列说法不正确的是?( )
A. 程序创建了2个实例
B. Student.count的值为0
C. study为该类的方法
D. 输出的结果为“小红在学习”
答案:B
解析:程序创建了2个实例,Student.count的值为2
考题10 单选题
下列不属于对象构成成份的是?( )
A. 标识
B. 属性
C. 方法(或操作)
D. 规则
答案:D
解析:对象是由描述该对象属性的数据以及可以对这些数据施加的所有操作封装在一起构成的统一体。一个对象通常可由对象名(标识)、属性和操作三部分组成。本题答案为D选项。
考题11 单选题
有如下程序段:
class xcal():
def __init__(self,numx,numy):
self.numx=numx
self.numy=numy
def xadd(self,another):
numx=self.numx*another.numx
numy=self.numy*another.numy
return xcal(numx,numy)
def print(self):
print(str(self.numx)+"/"+str(self.numy))
x=xcal(2,3)
y=x.xadd(xcal(4,5))
y.print()
程序运行后,输出的结果是?( )
A. 6/20
B. 15/8
C. 10/12
D. 8/15
答案:D
解析:根据题意得到输出的结果为str(self.numx)+"/"+str(self.numy),self.numx=24,self.numy=35,故答案选D
考题12 判断题
在面向对象编程中,类是用来描述具有相同属性和方法的对象的集合,它定义了该集合中每个对象共有的属性和方法。对象是类的实例,可以被赋予对象以独特的个性。( )
答案:正确
解析:在面向对象编程中,编写表示现实世界中的事物和情景的类,并基于类创建对象。编写类时,定义一类对象都有的通用行为。基于类创建对象时,每个对象都自动具备这种通用行为,然后可根据需要赋予每个对象独特的个性。根据类来创建对象被称为实例化,可以使用类的实例。
考题13 判断题
可以通过直接修改属性的值,及通过方法修改属性的值,这两种方法修改一个对象的属性,但是不能通过方法对属性的值进行递增/减。( )
答案:错误
解析:修改属性的值可以通过三种不同的方式:直接通过实例修改;通过方法设置;通过方法递增/递减(增加/减少特定的值)。
考题14 判断题
类中的函数称为方法,方法__init__() 是一个特殊的方法,开头和末尾各有两个下划线,这种约定避免Python默认方法与普通方法发生名称冲突。( )
答案:正确
解析:方法__init__的目的是为了防止调用冲突
考题15 判断题
类(class)由类名、属性和方法三个部分构成。( )
答案:正确
解析:类由类名、属性和方法三个部分构成
总结
面向对象重要组成部分
- 类
- 创建类
class 类名(): 代码
- 对象
对象名 = 类名()
- 类
添加对象属性
- 类外面
对象名.属性名 = 值
- 类里面
self.属性名 = 值
获取对象属性
- 类外面
对象名.属性名
- 类里面
self.属性名
魔法方法
__init__()
: 初始化__str__()
:输出对象信息__del__()
:删除对象时调用