一、类与对象
这里不多说什么拗口的理论了,说点个人理解吧,希望能帮小白理解。
在我一开始接触编程时,就觉得明明函数就已经是一种很好的封装了,程序不就应该用函数一点点的编出来嘛。实在是想不来怎么会还有更高级的封装方式——对象呢。可以说当时是一种完全想不来的状态,根本就不知道它存在的意义,不知道有没有和我一样的小伙伴,请在评论区举手示意,O(∩_∩)O哈哈~
后来,随着学习的深入,这才明白了对象存在的意义。其实这一切在学习数据结构时就埋下了伏笔。大家还记得什么线性表、栈、队列、二叉树、图吗?我们在学习这些的时候,老师是不是告诉过我们,这些东西都可以用抽象数据类型来表示,大致就是先定义一个容器,再定义一系列与它们的性质相匹配的操作。
比如对于栈,就有push(x),pop(),top()
而对于线性表则显然不存在这些操作。
这样我们就想到,对于一种容器,总有与之相匹配的一系列操作。故而,我们就可以用一种抽象度更高的封装方式——将容器与由它专属的一系列操作封装在一起。在这个封装后的代码块内,有表征容器属性的量,也有相应的函数可以对容器进行操作。比如,定义一个栈后,我们就可以直接对该栈进行push(x),pop(),top()
等操作,非常方便。相比于以前,我们现在把操作和对象绑定到一起了。
哦,对了,忘了解释,容器就按字面意思理解就可以了,就是一种装数据的装置,比如数组就是最基本、最简单的一种容器。
说到这里,如果你还不能理解这样做(面向对象)的好处究竟在哪的话,且看下面这个更直观的例子:
在现实世界中存在人这个对象,因此我们可以定义人这个类,而人能做什么呢,能吃、能睡、能学习。。。那么就可以定义如下:
class 人:
def 初始化(self, 姓名, 年龄,身高,饭量):
self.姓名 = 姓名
self.年龄 = 年龄
self.身高 = 身高
self.饭量 = 饭量
def 吃饭(self):
吃饭…
def 睡觉(self):
睡觉…
def 学习(self):
学习…
以后在程序里就可以直接使用这个人,并让他干相应的事,比如:
工具人 = 人(x, y, z, h) #x, y, z, h分别代表姓名、年龄、身高、饭量
工具人.吃饭()
工具人.睡觉()
工具人.学习()
更为方便的是,以后我们可以很方便的对人这个对象进行概念上的扩充。比如人分为男人和女人,男人和女人除了能吃饭、睡觉和学习外,还能XXOO(gkd~)。这样我们就可以创建两个新类,并从原有的类——人上继承所有属性和操作,并扩充新的、特有的属性。
class Male(人): #注意这里的两个知识点:1.python中约定类名首字母大写。
#2.继承时,在类名后加括号写上父类名,表示继承
def XX(self, XX的对象):
与XX的对象XXOO…
class Female(人): #人的其他属性全部继承
def OO(self, OO的对象):
与OO的对象XXOO…
# 下面开始使用
工具人 = Male(x, y, z, h) #x, y, z, h分别代表姓名、年龄、身高、饭量
工具人.吃饭()
工具人.睡觉()
工具人.学习()
工具人.XX()
现在感受到类的方便之处了嘛?下面我们说说具体的定义,弄明白对象和类的关系内涵。
所谓对象就是一类具体实物经抽象后形成的实例,比如上例中的人、男人、女人,他们都是对象,是程序的基本单元。他们具有自己的属性和一系列具体的方法(或者说是操作)。故而我们常说
对象 = 属性 + 方法
而类则是对象的模板,封装了对应的现实实体的性质和行为。直观的表现为上面的那一段class
后面缩进的代码
对象则是类的具体化,是用类这个模板造出的产品。
练习题
1、尝试执行以下代码,并解释错误原因:
class C:
def myFun():
print('Hello!')
c = C()
c.myFun()
程序执行后报错:NameError: name 'C' is not defined
原因分析:对类的调用放在了类定义里面,通俗的说就是说犯了禁止套娃(误。。。)的错误。此外,类里面的函数myFun()在定义时,应将self作为参数传入。改正后的代码如下:
class C:
def myFun(self):
print('Hello!')
c = C()
c.myFun()
output:
Hello!
2、按照以下要求定义一个游乐园门票的类,并尝试计算2个成人+1个小孩平日票价。
要求:
- 平日票价100元
- 周末票价为平日的120%
- 儿童票半价
写的有点繁,见谅!这里不难想象,传入类中的信息应该是一个三元组,即要体现(是平日还是周末,是大人还是小孩,有多少人),具体见下:
class ticket:
def __init__(self):
self.total = 0
self.info = {"星期%d" % (i, ):{"儿童":0, "大人":0} for i in range(1,8)}
def add_people(self, day, people, num):
self.info["星期%d" % (day, )][people] += num
def sub_people(self, day, people, num):
self.info["星期%d" % (day, )][people] -= num
def result(self):
self.total = 0
for i in self.info:
if i in ["星期6", "星期7"]:
for j in self.info[i]:
if j == "儿童":
self.total += 60 * self.info[i][j]
else:
self.total += 120 * self.info[i][j]
else:
for j in self.info[i]:
if j == "儿童":
self.total += 50 * self.info[i][j]
else:
self.total += 100 * self.info[i][j]
return self.total
t = ticket()
t.add_people(1,"大人", 2)
t.add_people(1,"儿童", 1)
print(t.result())
output:
250
二、魔法方法
插句题外话,有没有和我一样总把魔法方法看成魔方方法的小伙伴o(╥﹏╥)o,有的话麻烦在评论区留个言,我们一起报团取暖,么么哒~
练习题
1、上面提到了许多魔法方法,如
__new__,__init__, __str__,__repr__,__getitem__,__setitem__
等等,请总结它们各自的使用方法。
请读者自行总结(危。。。)
2、利用python做一个简单的定时器类
要求:
- 定制一个计时器的类。
- start和stop方法代表启动计时和停止计时。
- 假设计时器对象t1,print(t1)和直接调用t1均显示结果。
- 当计时器未启动或已经停止计时时,调用stop方法会给予温馨的提示。
- 两个计时器对象可以进行相加:t1+t2。
- 只能使用提供的有限资源完成。
未完待续(危。。。)