面向对象进阶

一 综合案例

  1. 案例1 :简单的计算器
# 实现一个计算器可以进行简单的基本操作,以及打印结果def jia(n1, n2):return n1 + n2def jian(n1, n2):return n1 - n2def cheng(n1, n2):return n1 * n2a = jia(1, 2)
print(a)#例如计算(6+2-4)*5
n1 = jia(6,2)
n2 = jian(n1,4)
n3 = cheng(n2,5)
print(n3)

  • 改进1

我们可以看到上面的综合计算的步骤太麻烦了,还得计算中间结果,下面我们来对它进行改进

下面的改进实际上是用了result来保存中间结果,这样调用的函数的时候就简单一点

result = 0
def first_value(v):global resultresult = v
def jia(n):# 为了能让result可以在函数中修改,把他变成全局变量global resultresult += n
def jian(n):global resultresult -= n
def cheng(n):global resultresult *= n# 计算(2+6-4)*5
first_value(2)
jia(6)
jian(4)
cheng(5)
print(result)

  • 改进2

我们上面的方式可以说是面向过程的,我们可以看到首先就是很麻烦,其次就是result是全局变量,如果我们在下面把这个全局变量修改会导致结果错误,那我们把这个计算器的功能给修改了,使用面向对象的方法进行封装。

下面我们对这个代码来进行分析,我们封装之后面临着两个问题就是首先这个result是变量,那面向对象处理之后就是把它作为属性,是类属性还是实例(对象属性)呢,我们把它设置为类属性,这样就可以通过类.属性名来进行调用;第二个问题就是函数我们怎么样修改呢,修改成方法有三个选择,类方法,实例方法和静态方法,如果要使用对象方法或者说是实例方法的时候,会传个self,但是呢这个self没有什么作用,而且我们想要调用的时候可以直接使用类.方法就行,所以我们选用的是类方法;并且为了这个全局变量result不能别修改,我们将它私有化,但是私有化了之后不能去访问,所以我们加了一个show方法,可以展示result结果。

class Caculator():__result = 0@classmethoddef first_value(cls,v):cls.__result = v@classmethoddef jia(cls,n):cls.__result += n@classmethoddef jian(cls,n):cls.__result -= n@classmethoddef cheng(cls,n):cls.__result *= n@classmethoddef show(cls):print(f'计算的结果是:{cls.__result}')Caculator.first_value(2)
Caculator.jia(6)
Caculator.jian(4)
Caculator.cheng(5)
Caculator.show()

  • 改进3

这个result是类属性,它是依附于类的,这个类是在内存中只有一个的,所以result无法进行同时进行多个表达式的运算;同时操作的时候是同一个对象;如果变成对象属性就可以避免这种情况,同时一旦result属性变成对象属性,下面的方法也要变成实例方法

class Caculator():def __init__(self,result):self.__result = resultdef first_value(self,v):self.__result = vdef jia(self,n):self.__result += ndef jian(self,n):self.__result -= ndef cheng(self,n):self.__result *= ndef show(self):print(f'计算的结果是:{self.__result}')c1 = Caculator(2)
c1.jia(6)
c1.jian(4)
c1.cheng(5)
c1.show()

  • 改进4-加上验证、容错处理
class Caculator():def check_num(self,num):if not isinstance(num,int):raise TypeError('您输入的类型错误')def __init__(self,result):self.check_num(result)self.__result = resultdef first_value(self,v):self.check_num(v)self.__result = vdef jia(self,n):self.check_num(n)self.__result += ndef jian(self,n):self.check_num(n)self.__result -= ndef cheng(self,n):self.check_num(n)self.__result *= ndef show(self):print(f'计算的结果是:{self.__result}')c1 = Caculator('abc')
c1.jia(6)
c1.jian(4)
c1.cheng(5)
c1.show()

  • 改进5-上面的验证方法改变了代码原本的功能,我们可以使用装饰器来解决,可以加一些额外的功能,并且不会破坏原本的代码。

装饰器既可以通过类来实现,也可以通过函数来实现,这里先暂时先选择函数实现

class Caculator():#接受一个func,一个外界所传递过来的函数,也就是要装饰的函数def check_num_zsq(func):#内部呢,要定义一个闭包,要保证这个inner里面和下面加减乘所接受到的参数完全一样def inner(self,n):if not isinstance(n,int):raise TypeError('您输入的类型错误')#如果是整形,直接调用这个装饰的函数就可以了,并且把返回值返回给外界return func(self,n)#返回inner但是不要写小括号,小括号代表执行,不写小括号代表把函数给外界return inner@check_num_zsqdef __init__(self,result):self.__result = result@check_num_zsqdef first_value(self,v):self.__result = v@check_num_zsqdef jia(self,n):self.__result += n@check_num_zsqdef jian(self,n):self.__result -= n@check_num_zsqdef cheng(self,n):self.__result *= ndef show(self):print(f'计算的结果是:{self.__result}')c1 = Caculator(2)
c1.jia(6)
c1.jian(4)
c1.cheng(5)
c1.show()
  • 改进6,已经写好了装饰器,装饰器本质讲就是一个函数,而这个函数写在类的内部是属于一个实例方法,外界可以通过实例来调用这个装饰器,但是没有什么意义,并且没什么用;那么怎么样不让外界调用这个装饰器呢?直接把它变成一个私有的方法

外界访问

class Caculator():#接受一个func,一个外界所传递过来的函数,也就是要装饰的函数def __check_num_zsq(func):#内部呢,要定义一个闭包,要保证这个inner里面和下面加减乘所接受到的参数完全一样def inner(self,n):if not isinstance(n,int):raise TypeError('您输入的类型错误')#如果是整形,直接调用这个装饰的函数就可以了,并且把返回值返回给外界return func(self,n)#返回inner但是不要写小括号,小括号代表执行,不写小括号代表把函数给外界return inner@__check_num_zsqdef __init__(self,result):self.__result = result@__check_num_zsqdef first_value(self,v):self.__result = v@__check_num_zsqdef jia(self,n):self.__result += n@__check_num_zsqdef jian(self,n):self.__result -= n@__check_num_zsqdef cheng(self,n):self.__result *= ndef show(self):print(f'计算的结果是:{self.__result}')c1 = Caculator(2)
c1.jia(6)
c1.jian(4)
c1.cheng(5)
c1.show()
  • 改进7-新增功能,输入的时候,加上语音播报,主要问题是这个语音播报写在那个位置,并且如何调用

通过一个实例去调用某个实例方法,他会自动的往里面传第一个参数self,所以方法要加一个参数self

如果下面win32com出现包找不到代码,可以先通过下面操作进行导入

python -m pip install pypiwin32
import win32com.client
#1. 创建一个播报器对象
speaker = win32com.client.Dispatch('SAPI.SpVoice')
#2. 通过这个播报器对象,直接播放相对应的语音字符串即可
speaker.Speak('我的名字是sz')class Caculator():def __say(self,word):# 1. 创建一个播报器对象speaker = win32com.client.Dispatch('SAPI.SpVoice')# 2. 通过这个播报器对象,直接播放相对应的语音字符串即可speaker.Speak(word)#接受一个func,一个外界所传递过来的函数,也就是要装饰的函数def __check_num_zsq(func):#内部呢,要定义一个闭包,要保证这个inner里面和下面加减乘所接受到的参数完全一样def inner(self,n):if not isinstance(n,int):raise TypeError('您输入的类型错误')#如果是整形,直接调用这个装饰的函数就可以了,并且把返回值返回给外界return func(self,n)#返回inner但是不要写小括号,小括号代表执行,不写小括号代表把函数给外界return inner@__check_num_zsqdef __init__(self,result):self.__say(result)self.__result = result@__check_num_zsqdef first_value(self,v):self.__say(v)self.__result = v@__check_num_zsqdef jia(self,n):self.__say(n)self.__result += n@__check_num_zsqdef jian(self,n):self.__say(n)self.__result -= n@__check_num_zsqdef cheng(self,n):self.__say(n)self.__result *= ndef show(self):self.__say(f'计算的结果是:{self.__result}')print(f'计算的结果是:{self.__result}')c1 = Caculator(2)
c1.jia(6)
c1.jian(4)
c1.cheng(5)
c1.show()
  • 改进8-上面的代码还是一样的问题就是代码内容改了,所以为了保证代码的内容不被修改,我们继续可以使用装饰器来进行操作
class Caculator():#接受一个func,一个外界所传递过来的函数,也就是要装饰的函数def __check_num_zsq(func):#内部呢,要定义一个闭包,要保证这个inner里面和下面加减乘所接受到的参数完全一样def inner(self,n):if not isinstance(n,int):raise TypeError('您输入的类型错误')#如果是整形,直接调用这个装饰的函数就可以了,并且把返回值返回给外界return func(self,n)#返回inner但是不要写小括号,小括号代表执行,不写小括号代表把函数给外界return innerdef __say_zsq(func):def inner(self,n):# 1. 创建一个播报器对象speaker = win32com.client.Dispatch('SAPI.SpVoice')# 2. 通过这个播报器对象,直接播放相对应的语音字符串即可speaker.Speak(n)return func(self,n)return inner@__check_num_zsq@__say_zsqdef __init__(self,result):self.__result = result@__check_num_zsq@__say_zsqdef first_value(self,v):self.__result = v@__check_num_zsq@__say_zsqdef jia(self,n):self.__result += n@__check_num_zsq@__say_zsqdef jian(self,n):self.__result -= n@__check_num_zsq@__say_zsqdef cheng(self,n):self.__result *= ndef show(self):self.__say(f'计算的结果是:{self.__result}')print(f'计算的结果是:{self.__result}')c1 = Caculator(2)
c1.jia(6)
c1.jian(4)
c1.cheng(5)
c1.show()

二 面向对象的三大特性之封装

概念:将一些属性和方法封装到一个对象中,对外呢隐藏内部具体的实现细节


三 面向对象的三大特性之继承

继承父类非私有的属性和方法


class Animal():pass
class Person():pass
#单继承
class Dog(Animal):pass#多继承
class Monkey(Animal,Person):pass

  • 继承的资源的使用:私有的属性和方法都无法访问,也就是带两个下划线的都不能访问;并且在python里面所谓的继承是指子类能不能使用父类的资源,并不是复制。


class A:age = 9class B(A):passprint(A.age)
print(B.age)
B.age = 10
print(A.age)
print(B.age)
print(A.__dict__)
print(B.__dict__)

根据代码我们可以看出来,这个B.age相当于给B添加了一个属性,B继承了A,B可以读取A的资源而不是设置

  • 常见的继承形态


单继承:从下往上



对于多继承链:它会遵循单调原则,如上图所示,会优先先到左侧B找,B没有再到D;然后到右侧寻找

对于有重叠的多继承链:从下到上,A没有去优先左侧B,B没有再到C里面找,C里面没有到D里面去寻找

在Python2.2之前的版本中遵循的原则是深度优先遍历,违背了重写可用原则,尤其是对于有重叠的多继承链


  • 资源的覆盖

如下图所示,根据继承的规则我们知道访问顺序应该是A,B,C,D,B和C同时都有XXX,根据访问机制,C的XXX是无法覆盖掉B的XXX的。


注意事项:当调用优先级比较高的资源的时候,注意self的变化,比如说B里面有个实例方法,还有一个类方法,这时候A去调用这个两个方法,传进来的实际上是A,用实例a去调用呢,实际上也是A方法,也就是说谁调用这个方法传进来的就是谁。

  • 资源的累加

注意一点:比如A继承它的父类B的时候,B里面有一个init方法,里面会有个实例属性a,A创建的对象可以访问这个a,这时候给A也加上一个init方法,里面有个属性b,这时候用对象访问a就不可以了,就会报错;因为对于对象a来说先进行访问A类的init方法,不会访问B的init方法,所以没有属性a

class B:age = 18def __init__(self):self.a = 1class A(B):def __init__(self):self.b = 2aa = A()
print(aa.b)
print(aa.a)


其实可以避免这个情况就是用super(还有在A里面加一个用类B调用init,然后把self传进去的方式,但是不利于代码的维护)

下面介绍super的用法



这里的第二个参数一般是前一个参数的子类,或者实例对象,但是这个实例对象对应的类应该是第一个参数的子类或者第一个参数的本身;第一个参数是找谁的下一个节点。

class B:a = 1def __init__(self):self.b = 2def t1(self):print('t1')@classmethoddef t2(cls):print('t2')@staticmethoddef t3():print('t3')class A(B):c = 3def __init__(self):# 这里我们看到要找A的下一个节点,第一个参数就传A#对于第二个参数看沿着谁的链条,不能是B,因为B之后没有A了,只能是A(A的对象)或者A的子类(A子类对象)#对于B的init这个方法,我们到时候要给这个实例方法里面传递参数self,是个实例对象;要是调用类方法传递的就是类了#A的实例对象就是self# super(A, self).__init__()#上面的写法等同于下面,简化了会自动判断填充的super().__init__()self.e = 6def tt1(self):print('tt1')@classmethoddef tt2(cls):#这里因为是类方法(往B里面传的是类),所以第二个参数传入的是类,只能是A或者A的子类,这里只能写Asuper(A,A).t2()print('tt2')@staticmethoddef tt3():print('tt3')a = A()
print(a.__dict__)
A.tt2()


根据结果我们可以看到是成功的

下面再看菱形结构,就是A的父类是两个的情况,我们说用类调用的方式会产生重复调用,所以才引入super方法,下面我们看一下super方法在菱形结构的调用

class D():def __init__(self):print('d')class B(D):def __init__(self):D.__init__(self)print('b')class C(D):def __init__(self):D.__init__(self)print('c')class A(B,C):def __init__(self):B.__init__(self)C.__init__(self)print('a')A()


我们可以看到之前的方法D类会被调用两次,下面我们用super的方式来解决下

class D():def __init__(self):print('d')class B(D):def __init__(self):super().__init__()print('b')class C(D):def __init__(self):super().__init__()print('c')class A(B,C):def __init__(self):# B.__init__(self)# C.__init__(self)super().__init__()print('a')A()

注意事项:不要尝试通过实例对象self动态的去获取他的类,因为到时候这个实例对象self’到底是谁是不知道的,如果他是父类,有可能是它子类的实例对象,所以有可能会产生死循环;第二个注意事项到时候在子类里面尝试去调用父类资源的时候,不要把用类名来调用和通过super来调用进行混合使用,因为到时候有可能会产生一些混乱

四 面向对象的三大特性之多态

多态就是一个类所延伸的多种形态

下面这个例子,方法都是一样的,只是所接受的对象不一样,对象拥有不同的形态就产生了不同的行为。


class Animal():def jiao(self):passclass Dog(Animal):def jiao(self):print('汪汪汪')class Cat(Animal):def jiao(self):print('喵喵喵')def test(obj):obj.jiao()d = Dog()
c = Cat()
test(c)


  • 抽象类和抽象方法


对于上面的例子来说,Animal就是一个抽象的类,你可以说创建一个小狗对象,一个小猫对象这样的具体的对象,而不能说创建一个Animal对象,而叫就是一个抽象方法

import abcclass Animal(object,metaclass=abc.ABCMeta):@abc.abstractmethoddef jiao(self):pass@abc.abstractclassmethoddef test(cls):passclass Dog(Animal):def jiao(self):print('汪汪汪')@classmethoddef test(cls):print('xxx')
class Cat(Animal):def jiao(self):print('喵喵喵')def test(obj):obj.jiao()d = Dog()
d.jiao()
d.test()

五 综合案例

先定义小狗类:

class Dog:#age设置为如果不写年龄默认为1def __init__(self,name,age = 1):self.name = nameself.age = agedef eat(self):print(f'叫{self.name},{self.age}岁的小狗正在吃东西')def play(self):print(f'叫{self.name},{self.age}岁的小狗正在玩')def sleep(self):print(f'叫{self.name},{self.age}岁的小狗正在睡觉')def watch(self):print(f'叫{self.name},{self.age}岁的小狗正在看家')d = Dog('小黑',10)
d.play()

  • 改进1,我们可以看到之前写的print函数里面好多是重复的,所以我们可以选用之前学的str来提取公共的话语来简化,str表示self字符串本身它的字符串描述,到时候只需要把这个字符串返回出去即可
class Dog:#age设置为如果不写年龄默认为1def __init__(self,name,age = 1):self.name = nameself.age = agedef eat(self):print(f'{self}吃东西')def play(self):print(f'{self}玩')def sleep(self):print(f'{self}睡觉')def watch(self):print(f'{self}看家')def __str__(self):return f'叫{self.name},{self.age}的小狗正在'
d = Dog('小黑',10)
d.play()

  • 改进二:加上猫和人这两个类别
class Person:#这种存在默认值的放在最后面def __init__(self,name,pets,age = 18):self.name = nameself.age = ageself.pets = petsdef eat(self):print(f'{self}吃东西')def play(self):print(f'{self}玩')def sleep(self):print(f'{self}睡觉')#其实就是让所有的宠物吃饭,睡觉,玩def carePets(self):for pet in self.pets:pet.eat()pet.play()pet.sleep()#遍历拥有的宠物,根据宠物的类别不同让宠物进行各自的工作def make_pets_work(self):for pet in self.pets:if isinstance(pet,Dog):pet.watch()elif isinstance(pet,Cat):pet.catch()def __str__(self):return f'叫{self.name},{self.age}的小狗正在'class Cat:#age设置为如果不写年龄默认为1def __init__(self,name,age = 1):self.name = nameself.age = agedef eat(self):print(f'{self}吃东西')def play(self):print(f'{self}玩')def sleep(self):print(f'{self}睡觉')def catch(self):print(f'{self}捉老鼠')def __str__(self):return f'叫{self.name},{self.age}的小猫正在'class Dog:#age设置为如果不写年龄默认为1def __init__(self,name,age = 1):self.name = nameself.age = agedef eat(self):print(f'{self}吃东西')def play(self):print(f'{self}玩')def sleep(self):print(f'{self}睡觉')def watch(self):print(f'{self}看家')def __str__(self):return f'叫{self.name},{self.age}的小狗正在'd = Dog('小黑',10)
c = Cat('小红',6)
p = Person('sz',[d,c],18)
p.carePets()
p.make_pets_work()

  • 改进三:我们知道如果一个人拥有很多类别的宠物的时候,这个代码就要继续加判断,很麻烦,所以我们在宠物的方法中统一成work方法,这样就不用判断了
  • 改进四:每个类有很多是相同的,所以我们可以使用三大特性之继承特性,写一个父类,把公共方法什么的写到父类里面去,然后用别的去继承,再加上各自的单独的属性即可

属性:公共的:姓名、年龄;人有个特殊的属性:宠物
方法:公共的:吃饭、谁家、玩;各自的:狗:看家;猫:捉老鼠;人:养宠物和让宠物工作

class Animial():def __init__(self, name, age=18):self.name = nameself.age = agedef eat(self):print(f'{self}吃东西')def play(self):print(f'{self}玩')def sleep(self):print(f'{self}睡觉')class Person(Animial):#加super是继承父类的属性def __init__(self,name,pets,age = 18):super(Person,self).__init__(name,age)self.pets = petsdef carePets(self):for pet in self.pets:pet.eat()pet.play()pet.sleep()#遍历拥有的宠物,根据宠物的类别不同让宠物进行各自的工作def make_pets_work(self):for pet in self.pets:pet.work()def __str__(self):return f'叫{self.name},{self.age}的小狗正在'class Cat(Animial):def work(self):print(f'{self}捉老鼠')def __str__(self):return f'叫{self.name},{self.age}的小猫正在'class Dog(Animial):def work(self):print(f'{self}看家')def __str__(self):return f'叫{self.name},{self.age}的小狗正在'd = Dog('小黑',10)
c = Cat('小红',6)
p = Person('sz',[d,c],18)
p.carePets()
p.make_pets_work()

五 设计原则


单一职责:一个类只负责一项职责




就是任何地方你都可以使用子类对象去替代这个父类对象

例如下面这个例子,父类是小鸟会飞,但是子类鸵鸟是不会飞的,又想让鸵鸟继承小鸟,怎么办呢?把小鸟分成会飞的小鸟和不会飞的小鸟,让鸵鸟去继承不会飞的小鸟即可。


Python基础内容训练11(面向对象进阶)相关推荐

  1. Python基础十五:面向对象编程四:高级特性

    Python基础十五:面向对象编程四:高级特性 Python基础系列内容为学习廖雪峰老师Python3教程的记录,廖雪峰老师官网地址:廖雪峰Python3教程 Author:yooongchun Em ...

  2. python基础内容表达式_01-python基础之python介绍、三元表达式

    本节参考博客 备注:本系列博客除特别标注,默认的环境为python3 学习方法 1. 上课 上课听课的时候只认真听讲,跟着老师走,不要做笔记,如果有重要的知识点,在练习时间再做对应的练习,把重要的知识 ...

  3. Python开发系列课程(11) - 面向对象编程进阶

    面向对象编程进阶 在前面的章节我们已经了解了面向对象的入门知识,知道了如何定义类,如何创建对象以及如何给对象发消息.为了能够更好的使用面向对象编程思想进行程序开发,我们还需要对Python中的面向对象 ...

  4. Python基础笔记(1-1)

    笔记来源小甲鱼零基础入门 (笔记都做了!求求你多复习吧!干点儿人应该干的事儿!) 文章目录 一. 000. 愉快的开始 - 002. 设计第一个游戏 1. Python的应用范围 2. Python的 ...

  5. JAVA基础第三章 面向对象进阶--多态等

    3.1变量及其传递 1.变量实际是内存空间.引用型变量里存储的是引用,可以理解为对象实体的地址.指针.句柄.总之,通过这个引用我们就可以操纵这个对象. 例: MyDate m,n;  //定义两个My ...

  6. Python基础之函数,面向对象

    文章目录 1 函数 1.1 定义一个函数 1.2 函数调用 1.3 参数 1.3.1 必需参数 1.3.2 关键字参数 1.3.3 默认参数 1.3.4 不定长参数 1.3.4.1 不定长 *:元组 ...

  7. python基础笔记二_面向对象

    面向对象 Object Oriented 概述 面向过程 1.分析出解决问题的步骤,然后逐步实现.例如:婚礼筹办-- 发请柬(选照片.措词.制作)-- 宴席(场地.找厨师.准备桌椅餐具.计划菜品.购买 ...

  8. 【python基础】4.1 面向对象(待修改)

    一.名词介绍 1.1 面向对象与面向过程 解决问题的两种思路 面向对象 面向过程 概念 将构成问题的事物,分解成若干个对象,把数据以及对数据的操作放在一起,作为一个相互依存的整体 比如moba游戏里每 ...

  9. Python基础第十三节—面向对象(中)

    文章目录 一.封装 二.property装饰器 三.继承 四.方法重写 五.super() 六.多重继承 一.封装 封装是面向对象的三大特性之一,为了安全简化的编程,我们将抽象得到的数据和行为(或功能 ...

最新文章

  1. 菱形开合的实现 IOS
  2. queueing 优化_简单聊聊网页的资源加载优化
  3. js弹框带传值父窗口给子框_JavaScript实现弹出子窗口并传值给父窗口
  4. 一本很好的书LearnOpenGL
  5. 我用C++复刻了这款上世纪最伟大的游戏
  6. 黑客攻防技术宝典Web实战篇第2版—第2章核心防御机制
  7. Redmi K50系列年后登场:最高搭载天玑9000+2K直屏
  8. tf卡量产工具万能版_手上还有SD卡/TF卡的小伙伴,这些玩法你有关注过吗
  9. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(十九)ES6.2.2 安装Ik中文分词器
  10. 实验3-7 统计学生成绩 (15 分)
  11. SCM introduction
  12. CTU 2019 Open Contest G.Beer Mugs
  13. .net 根据银行卡获取银行信息
  14. 【kmp专题】牛客网子串(进制转化+kmp/string.find())
  15. 《软件创富----共享软件创业之道》读后感
  16. 云计算 概念 是什么
  17. 5个视频剪辑必用网站
  18. 查看已知WiFi网络的密码
  19. python标准库:fractions有理数
  20. DOS、Mac 和 Unix 文件格式

热门文章

  1. 黑金开发板在NiosII环境下烧写image到flash失败的解决办法
  2. 开心工作,开心生活!
  3. 信息编码--区位码,国标码,内码
  4. 高光谱图像分类(一)入门
  5. 三维激光扫描技术在工程领域中的应用
  6. 强化学习系列5:有模型的策略迭代方法
  7. sparkstreaming处理kafka数据,数据积压问题解决方案
  8. 微信小程序---家庭记账本开发(四)
  9. Android:rk3588 kernel单编
  10. 前端的小激动:Nodejs写简单接口教程