封装: 如何组织类或模块,让封装的类或组件,尽量只负责一个领域的工作
继承: 复用方式之一,概念形成统一。通过继承可以管理多个概念
多态: 类、方法等的行为不同的做法。目标一致,实现的方式不同

继承

初识继承

继承指的是类与类之间的关系,是一种什么“是”什么的关系,继承的功能之一就是用来解决代码重用问题
继承是一种创建新类的方式,在python中,新建的类可以继承一个或多个父类,父类又可以成为基类或超类,新建的类称为派生类或子类

python中类的继承分为:单继承和多继承

  1. class ParentClass1: #定义父类
  2. pass
  3. class ParentClass2: #定义父类
  4. pass
  5. class SubClass1(ParentClass1): #单继承,基类是ParentClass1,派生类是SubClass
  6. pass
  7. class SubClass2(ParentClass1,ParentClass2): #python支持多继承,用逗号分隔开多个继承的类
  8. pass

查看继承

  1. >>> SubClass1.__bases__ #__base__只查看从左到右继承的第一个子类,__bases__则是查看所有继承的父类
  2. (<class '__main__.ParentClass1'>,)
  3. >>> SubClass2.__bases__
  4. (<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

经典类与新式类(关于新式类与经典类的区别,我们稍后讨论)

  1. 1.只有在python2中才分新式类和经典类,python3中统一都是新式类
  2. 2.python2中,没有显式的继承object类的类,以及该类的子类,都是经典类
  3. 3.python2中,显式地声明继承object的类,以及该类的子类,都是新式类
  4. 4.python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类

提示:如果没有指定基类,python的类会默认继承object类,object是所有python类的基类,它提供了一些常见方法(如str)的实现。

  1. >>> ParentClass1.__bases__
  2. (<class 'object'>,)
  3. >>> ParentClass2.__bases__
  4. (<class 'object'>,)

继承与抽象

先抽象再继承,抽象即抽取共性

抽象分成两个层次:
1.将奥巴马和梅西这俩对象比较像的部分抽取成类;
2.将人,猪,狗这三个类比较像的部分抽取成父类。
抽象最主要的作用是划分类别(可以隔离关注点,降低复杂度)
05 面向对象的三大特性 - 图1

继承:是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。
抽象只是分析和设计的过程中,一个动作或者说一种技巧,通过抽象可以得到类

05 面向对象的三大特性 - 图2

属性查找

对象属性查找顺序,按照类的继承顺序自下而上(先从本类中找,找不到再从父类中找)

  1. # 属性查找小练习
  2. class Foo:
  3. def f1(self):
  4. print('from Foo.f1')
  5. def f2(self):
  6. print('from Foo.f2')
  7. self.f1() # b.f1()
  8. class Bar(Foo):
  9. def f1(self):
  10. print('from Bar.f1')
  11. b = Bar()
  12. # print(b.__dict__)
  13. b.f2()
  14. # from Foo.f2
  15. # from Bar.f1

派生

子类可以覆盖父类的属性和方法:一旦重新定义了自己的属性和方法且与父类重名,那么调用新增的属性时,就以自己为准了。

  1. class Hero:
  2. def __init__(self, nickname, life_value, aggresivity):
  3. self.nickname = nickname
  4. self.life_value = life_value
  5. self.aggresivity = aggresivity
  6. def attack(self, enemy):
  7. enemy.life_value -= self.aggresivity
  8. class Garen(Hero):
  9. camp = 'Demacia'
  10. def attack(self, enemy):
  11. print('from Garen Class')
  12. class Riven(Hero):
  13. camp = 'Noxus'
  14. g = Garen('草丛伦', 100, 30)
  15. r = Riven('锐雯雯', 80, 50)
  16. # print(g.camp)
  17. # g.attack(r)
  18. # print(r.life_value)
  19. g.attack(r)

继承的实现原理

python到底是如何实现继承的,对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表,例如

新式类

image.png

  1. # Python3
  2. class A:
  3. def f2(self):
  4. print("f2 from A")
  5. class B(A):
  6. def f1(self):
  7. print("from B")
  8. class C(A):
  9. def f2(self):
  10. print("f2 from C")
  11. class D(B, C):
  12. pass
  13. print(D.mro()) # D->B->C->A
  14. d = D()
  15. d.f2() # f2 from C

image.png
只有新式才有这个属性可以查看线性列表,经典类没有

经典类

image.png

  1. # Python2
  2. class A:
  3. def f2(self):
  4. print("f2 from A")
  5. class B(A):
  6. def f1(self):
  7. print("from B")
  8. class C(A):
  9. def f2(self):
  10. print("f2 from C")
  11. class D(B, C):
  12. pass
  13. # D->B->A->C
  14. d = D()
  15. d.f2() # f2 from A

image.png

  1. # Python2
  2. # coding:utf-8
  3. class A():
  4. def test(self):
  5. print('from A')
  6. class B(A):
  7. pass
  8. class C(A):
  9. def test(self):
  10. print('from C')
  11. class D(B):
  12. pass
  13. class E(C):
  14. def test(self):
  15. print('from E')
  16. class F(D, E):
  17. # def test(self):
  18. # print('from F')
  19. pass
  20. f1 = F()
  21. f1.test() # from A
  22. # 新式类继承顺序:F->D->B->E->C->A
  23. # 经典类继承顺序:F->D->B->A->E->C
  24. # python3中统一都是新式类
  25. # pyhon2中才分新式类与经典类

总结

定义的基类A最后访问,(除了A,从左到右,按列)
image.png
不知道你是否发现,如果用正经的C3算法规则去分析一个类继承关系有点繁琐,尤其是遇到一个复杂的类也要分析很久。
所以,我自己根据经验总结了一句话赠送给大家:从左到右,深度优先,大小钻石,留住顶端,基于这句话可以更快的找到继承关系。

  1. class M:
  2. pass
  3. class N:
  4. pass
  5. class E(M):
  6. pass
  7. class G:
  8. pass
  9. class K:
  10. pass
  11. class H(K):
  12. pass
  13. class D(G, H):
  14. pass
  15. class F(M, N):
  16. pass
  17. class P:
  18. pass
  19. class C(E, F):
  20. pass
  21. class B(D, E):
  22. pass
  23. class A(B, C, P):
  24. pass
  25. print(A.mro()) # 简写为:A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object

image.png

调用父类代码

  1. # 实例化对象
  2. class Hero:
  3. def __init__(self, nickname, life_value, aggresivity):
  4. self.nickname = nickname
  5. self.life_value = life_value
  6. self.aggresivity = aggresivity
  7. def attack(self, enemy):
  8. enemy.life_value -= self.aggresivity
  9. class Garen(Hero):
  10. camp = 'Demacia'
  11. def __init__(self, nickname, life_value, aggresivity, weapon):
  12. # 字段可不重写,调用父类__init__
  13. # self.nickname=nickname
  14. # self.life_value=life_value
  15. # self.aggresivity=aggresivity
  16. # 方式1.Hero.__init__(self, nickname, life_value, aggresivity)
  17. # 方式2.super(Garen, self).__init__(nickname, life_value, aggresivity)
  18. # 方式3.Python3简写(推荐):super().__init__(nickname, life_value, aggresivity)
  19. self.weapon = weapon
  20. def attack(self, enemy):
  21. super().attack(self, enemy) # 或者 指名道姓 Hero.attack(self, enemy)
  22. print('from Garen Class')
  23. g = Garen('草丛伦', 100, 30, '大宝剑')
  24. print(g.__dict__)
  25. # {'nickname': '草丛伦', 'life_value': 100, 'aggresivity': 30, 'weapon': '大宝剑'}
  1. # 对象交互
  2. class Hero:
  3. def __init__(self, nickname, life_value, aggresivity):
  4. self.nickname = nickname
  5. self.life_value = life_value
  6. self.aggresivity = aggresivity
  7. def attack(self, enemy):
  8. enemy.life_value -= self.aggresivity
  9. class Garen(Hero):
  10. camp = 'Demacia'
  11. def attack(self, enemy):
  12. super().attack(enemy) # 依赖继承
  13. print('from Garen Class')
  14. class Riven(Hero):
  15. camp = 'Noxus'
  16. g = Garen('草丛伦', 100, 30)
  17. r = Riven('锐雯雯', 80, 50)
  18. g.attack(r)
  19. print(r.life_value) # 50

组合

软件重用的重要方式除了继承之外还有另外一种方式,即:组合
组合指的是,在一个类中以另外一个类的对象作为数据属性,称为类的组合

  • 继承和另一个类的关系:is
  • 组合和另一个类的关系:has ```python

class People: school=’mufeng’

  1. def __init__(self,name,age,sex):
  2. self.name=name
  3. self.age=age
  4. self.sex=sex

class Teacher(People): def init(self,name,age,sex,level,salary,): super().init(name,age,sex)

  1. self.level=level
  2. self.salary=salary
  3. def teach(self):
  4. print('%s is teaching' %self.name)

class Student(People): def init(self, name, age, sex, classtime,): super()._init(name,age,sex)

  1. self.class_time=class_time
  2. def learn(self):
  3. print('%s is learning' % self.name)

class Course: def init(self,course_name,course_price,course_period): self.course_name = course_name self.course_price = course_price self.course_period = course_period

  1. def tell_info(self):
  2. print('课程名<%s> 课程价钱<%s> 课程周期<%s>' %(self.course_name,self.course_price,self.course_period))

teacher1=Teacher(‘ecithy’,18,’male’,10,3000,) teacher2=Teacher(‘mufeng’,28,’male’,30,3000,) python=Course(‘python’,3000,’3mons’) linux=Course(‘linux’,2000,’4mons’) teacher1.course=python teacher2.course=python

  1. <a name="blogTitle19"></a>
  2. ## 抽象类
  3. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/12405790/1612497591189-7f2c62ef-0c9a-4a78-9628-74f98393d199.png#height=118&id=Q2S5h&margin=%5Bobject%20Object%5D&name=image.png&originHeight=236&originWidth=1262&originalType=binary&ratio=1&size=93843&status=done&style=none&width=631)
  4. **作用**
  5. - 如果说类是从一堆对象中抽取相同的内容而来的,那么抽象类就是从一堆类中抽取相同的内容而来的,内容包括数据属性和函数属性。
  6. - 接口提取了一群类共同的函数,可以把接口当做一个函数的集合,然后让子类去实现接口中的函数。这么做的意义在于归一化,什么叫归一化,就是只要是基于同一个接口实现的类,那么所有的这些类产生的对象在使用时,从用法上来说都一样。
  7. **语法特点**:
  8. - 继承抽象类的类,必须实现抽象类所有的函数,当然可以增加其它函数
  9. - 抽象类只能被继承不能被实例化
  10. ```python
  11. import abc
  12. class Animal(metaclass=abc.ABCMeta): #只能被继承,不能被实例化
  13. all_type='animal'
  14. @abc.abstractmethod
  15. def run(self):
  16. pass
  17. @abc.abstractmethod
  18. def eat(self):
  19. pass
  20. # animal=Animal()
  21. class People(Animal):
  22. def run(self):
  23. print('people is running')
  24. def eat(self):
  25. print('people is eating')
  26. class Pig(Animal):
  27. def run(self):
  28. print('people is walking')
  29. def eat(self):
  30. print('people is eating')
  31. class Dog(Animal):
  32. def run(self):
  33. print('people is walking')
  34. def eat(self):
  35. print('people is eating')
  36. pig1=Pig()
  37. # dog1=Dog()
  38. # peo1.eat()
  39. # pig1.eat()
  40. # dog1.eat()
  41. #
  42. # print(peo1.all_type)

多态

多态指的是一类事物有多种形态,比如
多态性:指的是可以在不考虑对象的类型的情况下而直接使用对象
作用:
1.增加了程序的灵活性
  以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
2.增加了程序额可扩展性
 通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用  

  1. # 多态:同一类事物的多种形态
  2. import abc
  3. class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
  4. @abc.abstractmethod
  5. def talk(self):
  6. pass
  7. class People(Animal): #动物的形态之一:人
  8. def talk(self):
  9. print('say hello')
  10. class Dog(Animal): #动物的形态之二:狗
  11. def talk(self):
  12. print('say wangwang')
  13. class Pig(Animal): #动物的形态之三:猪
  14. def talk(self):
  15. print('say aoao')
  16. class Cat(Animal):
  17. def talk(self):
  18. print('say miamiao')
  19. # 多态性:指的是可以在不考虑对象的类型的情况下而直接使用对象
  20. peo1=People()
  21. dog1=Dog()
  22. pig1=Pig()
  23. cat1=Cat()
  24. # -------------------
  25. def func(instance, absCls=Animal):
  26. '依赖基类: 基类指向子类对象'
  27. absCls = instance
  28. return absCls
  29. func(peo1).talk()
  30. func(dog1).talk()
  31. func(pig1).talk()
  32. func(cat1).talk()
  33. # -------------------
  34. def func(animal):
  35. '不依赖基类'
  36. return animal
  37. func(peo1).talk()
  38. func(pig1).talk()
  39. func(dog1).talk()
  40. func(cat1).talk()

duck-typing

image.png
作用
除了多态的好处之外:不依赖基类,可以操作有不同基类的类,比多态更灵活

  1. class Disk:
  2. def read(self):
  3. print('disk read')
  4. def write(self):
  5. print('disk write')
  6. class Text:
  7. def read(self):
  8. print('text read')
  9. def write(self):
  10. print('text write')
  11. def myOpen(cls):
  12. '不依赖基类'
  13. return cls()
  14. f = myOpen(Disk)
  15. f.read()
  16. f.write()
  17. f = myOpen(Text)
  18. f.read()
  19. f.write()

Python内置类型用到了很多鸭子类型,如序列类型

  1. # 序列类型:列表list,元祖tuple,字符串str
  2. l=list([1,2,3])
  3. t=tuple(('a','b'))
  4. s=str('hello')
  5. # print(l.__len__())
  6. # print(t.__len__())
  7. # print(s.__len__())
  8. # def len(obj):
  9. # return obj.__len__()
  10. print(len(l))
  11. print(len(t))
  12. print(len(s))

封装

私有属性

语法:__X
作用:定义的私有属性和方法只能在本类和本类对象中使用,继承类和外部无法使用

  1. #其实这仅仅这是一种变形操作
  2. #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式:
  3. # 作用
  4. class A:
  5. __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
  6. def __init__(self):
  7. self.__X=10 #变形为self._A__X
  8. def __foo(self): #变形为_A__foo
  9. print('from A')
  10. def bar(self):
  11. self.__foo() #只有在类内部才可以通过__foo的形式访问到.
  12. #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形

这种变形的特点:
1、在类外部无法直接obj.AttrName
2、在类内部是可以直接使用:obj.
AttrName
3、子类无法覆盖父类__开头的属性

这种变形需要注意的问题是:

1、这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:类名_属性,然后就可以访问了,如a._A__N
2、变形的过程只在类的定义是发生一次,在定义后的赋值操作,不会变形
3、在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

  1. class A:
  2. def __foo(self): #_A__foo
  3. print('A.foo')
  4. def bar(self):
  5. print('A.bar')
  6. self.__foo() #self._A__foo()
  7. class B(A):
  8. def __foo(self): #_B__foo
  9. print('B.foo')
  10. b=B()
  11. b.bar()

封装的意义

一:封装数据

明确的区分内外,控制外部对隐藏的属性的操作行为

  1. class People:
  2. def __init__(self,name,age):
  3. self.__name=name
  4. self.__age=age
  5. def tell_info(self):
  6. print('Name:<%s> Age:<%s>' %(self.__name,self.__age))
  7. def set_info(self,name,age):
  8. if not isinstance(name,str):
  9. print('名字必须是字符串类型')
  10. return
  11. if not isinstance(age,int):
  12. print('年龄必须是数字类型')
  13. return
  14. self.__name=name
  15. self.__age=age
  16. p=People('ecithy',18)
  17. p.tell_info()
  18. p.set_info('ecithy',38)
  19. p.tell_info()
  20. p.set_info(123,38)
  21. p.set_info('ecithy','38')
  22. p.tell_info()

执行流程.gif

二:封装方法

目的是隔离复杂度

  1. 取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
  2. #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
  3. #隔离了复杂度,同时也提升了安全性
  4. class ATM:
  5. def __card(self):
  6. print('插卡')
  7. def __auth(self):
  8. print('用户认证')
  9. def __input(self):
  10. print('输入取款金额')
  11. def __print_bill(self):
  12. print('打印账单')
  13. def __take_money(self):
  14. print('取款')
  15. def withdraw(self):
  16. self.__card()
  17. self.__auth()
  18. self.__input()
  19. self.__print_bill()
  20. self.__take_money()
  21. a=ATM()
  22. a.withdraw()

执行流程.gif

property

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

  1. class People:
  2. def __init__(self,name):
  3. self.__name=name
  4. @property
  5. def name(self):
  6. # print('getter')
  7. return self.__name
  8. @name.setter
  9. def name(self,val):
  10. # print('setter',val)
  11. if not isinstance(val,str):
  12. print('名字必须是字符串类型')
  13. return
  14. self.__name=val
  15. @name.deleter
  16. def name(self):
  17. print('deleter')
  18. print('不允许删除')
  19. p=People('mufeng')
  20. # print(p.get_name())
  21. # print(p.name)
  22. # p.name
  23. p.name='mufeng'
  24. p.name=123
  25. # print(p.name)
  26. # del p.name

类中两大方法

在类内部定义的函数,分为两大类
一:绑定方法:绑定给谁,就应该由谁来调用,谁来调用就回把调用者当作第一个参数自动传入

绑定到对象的方法:在类内定义的没有被任何装饰器修饰的

  1. 绑定到类的方法:在类内定义的被装饰器classmethod修饰的方法

二:非绑定方法:没有自动传值这么一说了,就类中定义的一个普通工具,对象和类都可以使用
非绑定方法:不与类或者对象绑定

  1. # settings.py
  2. name='mufeng'
  3. age=18
  4. sex='female'
  1. import settings
  2. import hashlib
  3. import time
  4. class People:
  5. def __init__(self,name,age,sex):
  6. self.id=self.create_id()
  7. self.name=name
  8. self.age=age
  9. self.sex=sex
  10. def tell_info(self): #绑定到对象的方法
  11. print('Name:%s Age:%s Sex:%s' %(self.name,self.age,self.sex))
  12. @classmethod
  13. def from_conf(cls):
  14. obj=cls(
  15. settings.name,
  16. settings.age,
  17. settings.sex
  18. )
  19. return obj
  20. @staticmethod
  21. def create_id():
  22. m=hashlib.md5(str(time.time()).encode('utf-8'))
  23. return m.hexdigest()
  24. # p=People('mufeng',18,'male')
  25. # 绑定给对象,就应该由对象来调用,自动将对象本身当作第一个参数传入
  26. # p.tell_info() #tell_info(p)
  27. # 绑定给类,就应该由类来调用,自动将类本身当作第一个参数传入
  28. # p=People.from_conf() #from_conf(People)
  29. # p.tell_info()
  30. #非绑定方法,不与类或者对象绑定,谁都可以调用,没有自动传值一说
  31. p1=People('mufeng1',18,'male')
  32. p2=People('mufeng2',28,'male')
  33. p3=People('mufeng3',38,'male')
  34. print(p1.id)
  35. print(p2.id)
  36. print(p3.id)

反射

通过字符串的形式操作对象相关的属性

四个可以实现自省的函数 下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

hasattr(object,name)

判断object中有没有一个name字符串对应的方法或属性

getattr(object, name, default=None)

  1. def getattr(object, name, default=None): # known special case of getattr
  2. """
  3. getattr(object, name[, default]) -> value
  4. Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
  5. When a default argument is given, it is returned when the attribute doesn't
  6. exist; without it, an exception is raised in that case.
  7. """
  8. pass

setattr(x, y, v)

  1. def setattr(x, y, v): # real signature unknown; restored from __doc__
  2. """
  3. Sets the named attribute on the given object to the specified value.
  4. setattr(x, 'y', v) is equivalent to ``x.y = v''
  5. """
  6. pass

delattr(x, y)

  1. def delattr(x, y): # real signature unknown; restored from __doc__
  2. """
  3. Deletes the named attribute from the given object.
  4. delattr(x, 'y') is equivalent to ``del x.y''
  5. """
  6. pass
  1. # 反射:通过字符串映射到对象的属性
  2. class People:
  3. country='China'
  4. def __init__(self,name,age):
  5. self.name=name
  6. self.age=age
  7. def talk(self):
  8. print('%s is talking' %self.name)
  9. obj=People('mufeng',18)
  10. # 检测是否含有某属性
  11. print(hasattr(obj,'name')) # obj.name #obj.__dict__['name']
  12. print(hasattr(obj,'talk')) # obj.talk
  13. # print(getattr(obj,'namexxx',None))
  14. # print(getattr(obj,'talk',None))
  15. # 设置属性
  16. # setattr(obj,'sex','male') #obj.sex='male'
  17. # print(obj.sex)
  18. # 删除属性
  19. # delattr(obj,'age') #del obj.age
  20. # print(obj.__dict__)
  1. # 反射的应用:
  2. class Service:
  3. def run(self):
  4. while True:
  5. inp=input('>>: ').strip() #cmd='get a.txt'
  6. cmds=inp.split() #cmds=['get','a.txt']
  7. # print(cmds)
  8. if hasattr(self,cmds[0]):
  9. func=getattr(self,cmds[0])
  10. func(cmds)
  11. def get(self,cmds):
  12. print('get.......',cmds)
  13. def put(self,cmds):
  14. print('put.......',cmds)
  15. obj=Service()
  16. obj.run()