Python集训营45天—Day06 (面向对象编程基础)
目录
1. 面向对象编程基础
“把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。
简而言之,面向对象在我看来,把一个实体的属性抽象出来,然后去操作这个抽象的实体,这个实体可以有种种行为(方法),而不在是简单的单一结果。
面向对象编程的2个非常重要的概念:类和对象
对象是面向对象编程的核心,在使用对象的过程中,为了将具有共同特征和行为的一组对象抽象定义,提出了另外一个新的概念——类
类就相当于制造飞机时的图纸,用它来进行创建的飞机就相当于对象
类和对象之间的关系
面向对象有三大支柱:封装、继承和多态。
2. 定义类和创建对象
定义类:在python中可以使用class
关键字定义类,然后在类中通过之前学习过的函数来定义方法
定义一个类
class car: # 方法 def getcarinfo(self): print('车*个数:%d, 颜色%s'%(self.wheelnum, self.color)) def move(self): print("车正在移动...")
创建一个对象
# -*- coding:utf-8 -*- """ 定义一个类,再创建一个对象 version:0.1 author:coke """ #类 class car: #方法 def getcarinfo(self): print('车*个数:%d,颜色%s'%(self.wheelnum,self.color)) def move(self): print("车正在移动") bmw = car() bmw.color = "黑色" bmw.wheelnum = 4 bmw.getcarinfo() bmw.move()
注:这里出现了 self
,所谓的self
,可以理解为自己 ,某个对象调用其方法时,python解释器会把这个对象作为第一个参数传递给self
,所以开发者只需要传递后面的参数即可
3. __init__()
方法
在上一小节的demo中,我们已经给bmw这个对象添加了2个属性,wheelnum(车的轮胎数量)以及color(车的颜色),试想如果再次创建一个对象的话,肯定也需要进行添加属性,显然这样做很费事,那么有没有办法能够在创建对象的时候,就顺便把车这个对象的属性给设置呢?
# -*- coding:utf-8 -*- """ _init_使用 version:0.1 author:coke """ #定义汽车类 class car: def __init__(self): self.wheelnum = 4 self.color = '蓝色' def move(self): print('车在跑,目标:夏威夷') # 创建对象 bmw = car() print('车的颜色为:%s'%bmw.color) print('车轮胎数量为:%d'%bmw.wheelnum)
总结:当创建car对象后,在没有调用__init__()
方法的前提下,bmw就默认拥有了2个属性wheelnum和color,原因是__init__()
方法是在创建对象后,就立刻被默认调用了
4. 魔法方法
在python中方法名如果是__xxxx__()
的,那么就有特殊的功能,因此叫做“魔法”方法
当使用print输出对象的时候,只要自己定义了__str__(self)
方法,那么就会打印从在这个方法中return的数据
# -*- coding:utf-8 -*- """ _init_使用 version:0.1 author:coke """ #定义汽车类 class car: def __init__(self,newwheelnum,newcolor): self.wheelnum = newwheelnum self.color = newcolor def __str__(self): msg = "车轮胎数:" + str(self.wheelnum) + " 车的颜色:" + self.color return msg def move(self): print("车在跑,目标夏威夷") bwm = car(4,"green") bwm.move() print(bwm)
5. 访问可见性问题
在很多面向对象编程语言中,我们通常会将对象的属性设置为私有的(private)或受保护的(protected),简单的说就是不允许外界访问,而对象的方法通常都是公开的(public),因为公开的方法就是对象能够接受的消息。在python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头,下面的代码可以验证这一点。
# -*- coding:utf-8 -*- """ 测试可见性 version:0.1 author:coke """ class test: def __init__(self, foo): self.__foo = foo def __bar(self): print(self.__foo) print('__bar') def main(): test = test('hello') # attributeerror: 'test' object has no attribute '__bar' test.__bar() # attributeerror: 'test' object has no attribute '__foo' print(test.__foo) if __name__ == "__main__": main()
但是,python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来“妨碍”对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。之所以这样设定,可以用这样一句名言加以解释,就是“we are all consenting adults here”。因为绝大多数程序员都认为开放比封闭要好,而且程序员要自己为自己的行为负责。
# -*- coding:utf-8 -*- """ 测试可见性 version:0.2 author:coke """ class test: def __init__(self,foo): self.__foo = foo def __bar(self): print(self.__foo); print("__bar") def __str__(self): return self.__foo def main(): test = test("hello") print(test) #事实上还是可以在外部访问 __foo属性 print(test._test__foo) if __name__ == "__main__": main()
在实际开发中,我们并不建议将属性设置为私有的,因为这会导致子类无法访问(后面会讲到)。所以大多数python程序员会遵循一种命名惯例就是让属性名以单下划线开头来表示属性是受保护的,本类之外的代码在访问这样的属性时应该要保持慎重
5. 练习
为了更好的理解面向对象编程,下面以“烤地瓜”为案例,进行分析
示例属性如下:
- cookedlevel : 这是数字;0~3表示还是生的,超过3表示半生不熟,超过5表示已经烤好了,超过8表示已经烤成木炭了!我们的地瓜开始时时生的
- cookedstring : 这是字符串;描述地瓜的生熟程度
- condiments : 这是地瓜的配料列表,比如番茄酱、芥末酱等
示例方法如下:
-
cook()
: 把地瓜烤一段时间 -
addcondiments()
: 给地瓜添加配料 -
__init__()
: 设置默认的属性 -
__str__()
: 让print的结果看起来更好一些
- 1、定义类,并且定义__init__()方法
#定义`地瓜`类 class sweetpotato: '这是烤地瓜的类' #定义初始化方法 def __init__(self): self.cookedlevel = 0 self.cookedstring = "生的" self.condiments = []
- 2、添加"烤地瓜"方法
#烤地瓜方法 def cook(self, time): self.cookedlevel += time if self.cookedlevel > 8: self.cookedstring = "烤成灰了" elif self.cookedlevel > 5: self.cookedstring = "烤好了" elif self.cookedlevel > 3: self.cookedstring = "半生不熟" else: self.cookedstring = "生的"
- 3、基本的功能已经有了一部分,赶紧测试一下
mysweetpotato = sweetpotato() print(mysweetpotato.cookedlevel) print(mysweetpotato.cookedstring) print(mysweetpotato.condiments)
- 4、定义addcondiments()方法和__str__()方法
def __str__(self): msg = self.cookedstring + " 地瓜" if len(self.condiments) > 0: msg = msg + "(" for temp in self.condiments: msg = msg + temp + ", " msg = msg.strip(", ") msg = msg + ")" return msg def addcondiments(self, condiments): self.condiments.append(condiments)
全部代码
# -*- coding:utf-8 -*- """ 烤地瓜 """ class sweetpotato: "这是烤地瓜的类" #定义初始化方法 def __init__(self): self.cookedlevel = 0 self.cookedstring = "生的" self.condiments = [] #添加烤地瓜的方法 def cook(self,time): self.cookedlevel += time if self.cookedlevel > 10: self.cookedstring = "烤成灰了" elif self.cookedlevel > 5: self.cookedstring = "烤好了" elif self.cookedlevel > 3: self.cookedstring = "半生不熟" else: self.cookedstring = "生的" #添加配料 def addcondiments(self,condiment): self.condiments.append(condiment) def __str__(self): msg = self.cookedstring + "地瓜" if len(self.condiments) > 0: msg = msg + "(" for temp in self.condiments: msg = msg + temp + ", " msg = msg.strip(", ") msg = msg + ")" return msg mysweetpotato = sweetpotato() print("------有了一个地瓜,还没有烤--------") print(mysweetpotato.cookedlevel) print(mysweetpotato.cookedstring) print(mysweetpotato.condiments) print("---------接下来进行烤地瓜------------") print("---------地瓜烤了四分钟------------") mysweetpotato.cook(4) print(mysweetpotato) print("---------地瓜又烤了分钟-----------") mysweetpotato.cook(3) print(mysweetpotato) print("----------添加配料番茄酱------------") mysweetpotato.addcondiments("番茄酱") mysweetpotato.addcondiments("芥末酱") mysweetpotato.cook(2) print(mysweetpotato)
输出结果