图片 47

Python 全栈开发:python面向对象三大特征

三、多态

#多态
class Animal:       #同一类事物:Animal
    pass

class Pig(Animal):  #Animal的形态之一:Pig
    def eat(self):
        print('Pig is eating')

class Dog(Animal):  #Animal的形态之二:Dog
    def eat(self):
        print('Dog is eating')

#多态性:在不考虑对象具体类型的情况下,直接使用对象
pig1=Pig()
dog1=Dog()
pig1.eat()
dog1.eat()
#多态性的好处:增加了程序的灵活性和扩展性

5.派生与重用

派生:子类定义自己新的属性,如果与父类同名,以子类自己的为准

图片 1图片 2

# 父类
class People:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

    def func1(self):
        print('People.func1')


# 子类 派生自己的属性
class Teacher(People):
    def __init__(self, name, age, sex, level, salary):
        self.name = name
        self.age = age
        self.sex = sex

        self.level = level
        self.salary = salary

    def func1(self):
        print('Teacher.func1')


# 实例化
tea1 = Teacher('fixd', 18, 'male', 9, 3.1)
print(tea1.name, tea1.age, tea1.sex, tea1.level, tea1.salary)

# 结果
fixd 18 male 9 3.1

示例代码

重用:在子类派生出的新方法中重用父类的功能

方式一:指名道姓地调用(其实与继承没有什么关系的)

# 父类
class People:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


# 子类 "指名道姓" 调用父类的属性
class Teacher(People):
    def __init__(self, name, age, sex, level, salary):
        People.__init__(self, name, age, sex)
        self.level = level
        self.salary = salary

方式二、super()调用(严格依赖于继承)

*  ps:super()的返回值是一个特殊的对象,该对象专门用来调用父类中的属性*

*  了解:在python2中,需要super(自己的类名,self)*

# 父类
class People:
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex


# 子类 super() 调用父类的属性
class Teacher(People):
    def __init__(self, name, age, sex, level, salary):       
        super().__init__(name, age, sex)
        self.level = level
        self.salary = salary 

注意:以上两种方式都可以使用,在实际的编码工作中,推荐使用统一的一种方式

 

图片 3图片 4

#super()会严格按照mro列表从当前查找到的位置继续往后查找
class A:
    def test(self):
        print('A.test')
        super().f1()
class B:
    def f1(self):
        print('from B')
class C(A,B):
    pass

c=C()
print(C.mro()) #C->A->B->object


c.test()

mro列表

 

  属性隐藏

2.为什么要用封装

封装的真谛在于明确内外

对外隐藏(类的外部只能通过我们提供的接口对类内部的隐藏属性就行访问)

对内开放(在类的内部可以直接使用隐藏属性)

 

封装数据(变量)将数据隐藏并不是我们的目的,可以通过接口的方式将数据暴露给类外面使用,在接口中我们可以对数据进行限制,完成对数据的控制

图片 5图片 6

class Teacher:
    def __init__(self,name,age):
        # self.__name=name
        # self.__age=age
        self.set_info(name,age)

    def tell_info(self):
        print('姓名:%s,年龄:%s' %(self.__name,self.__age))
    def set_info(self,name,age):
        if not isinstance(name,str):
            raise TypeError('姓名必须是字符串类型')
        if not isinstance(age,int):
            raise TypeError('年龄必须是整型')
        self.__name=name
        self.__age=age


t=Teacher('egon',18)
t.tell_info()

t.set_info('egon',19)
t.tell_info()

示例代码

封装方法(函数)主要目的隔离复杂度,将类中多个函数组合,提供一个对外封装好的接口,供使用者调用,而调用者无需考虑接口内复杂的实现过程,简化调用

图片 7图片 8

#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
#对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做
#隔离了复杂度,同时也提升了安全性

class ATM:
    def __card(self):
        print('插卡')
    def __auth(self):
        print('用户认证')
    def __input(self):
        print('输入取款金额')
    def __print_bill(self):
        print('打印账单')
    def __take_money(self):
        print('取款')

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()

a=ATM()
a.withdraw()

隔离复杂度的例子

示例代码

图片 9图片 10

2、为什么要用多态   

用基类创建一套统一的规则,强制子类去遵循(使用抽象类实现),这样便可以
在不用考虑对象具体类型的前提下而直接使用对象下的方法

  多态性的好处

4.继承与抽象(先抽象再继承)

先抽象:抽取对象之间相似之处得到了类,在总结类与类之间的相似得到父类

再继承:(子类继承父类,子类可以遗传父类属性)是基于抽象的结果,通过编程语言去实现它,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构。

  鸭子类型:如果看起来像、叫声像而且走起路来像鸭子那么它就是鸭子

4、多态性

1.什么是多态动态绑定(在继承的背景下使用时,又称为多态性)

多态性是指在不考虑实力类型的情况下使用实例

图片 11图片 12

在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func,又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同

解释

2.为什么要用多态性(多态性的好处)

  • 增加了程序的灵活性

    以不变应万变,不论对象千变万化,使用者都是同一种形式去调用

  • 增加了程序的可扩展性

    通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用

图片 13图片 14

>>> class Cat(Animal): #属于动物的另外一种形态:猫
...     def talk(self):
...         print('say miao')
... 
>>> def func(animal): #对于使用者来说,自己的代码根本无需改动
...     animal.talk()
... 
>>> cat1=Cat() #实例出一只猫
>>> func(cat1) #甚至连调用方式也无需改变,就能调用猫的talk功能
say miao

'''
这样我们新增了一个形态Cat,由Cat类产生的实例cat1,使用者可以在完全不需要修改自己代码的情况下。使用和人、狗、猪一样的方式调用cat1的talk方法,即func(cat1)
'''

示例代码

 

3.鸭子类型

Python中崇尚鸭子类型(如果看起来像,叫声像而且走起路像鸭子,那么它就是鸭子)

通过继承实现的多态性,具有强耦合性;

鸭子类型,通过创建一个外观和行为像,但与原类型毫无关系的全新对象,具有松耦合性;

图片 15图片 16

#其实大家一直在享受着多态性带来的好处,
#比如Python的序列类型有多种形态:字符串,列表,元组,多态性的体现
#
#str,list,tuple都是序列类型
s=str('hello')
l=list([1,2,3])
t=tuple((4,5,6))

#我们可以在不考虑三者类型的前提下使用s,l,t
s.__len__()
l.__len__()
t.__len__()

len(s)
len(l)
len(t)

示例代码

 

class People:
    def __init__(self,name,age,date_obj):
        self.name=name
        self.age=age
        self.birth=date_obj

    def tell_info(self):
        print('name:%s--age:%s'%(self.name,self.age))

class Student(People):
    def learn(self):
        print('%s is learning'%self.name)

    def tell_info(self):
        print('student:',end='')
        People.tell_info(self)

class Date:
    def __init__(self,year,mon,date):
        self.year=year
        self.mon=mon
        self.date=date

    def tell_birth(self):
        print('birth day is <%s-%s-%s>'%(self.year,self.mon,self.date))


day1=Date(1990,12,12)
#day1.tell_birth()
stu1=Student('lary',18,day1)
stu1.birth.tell_birth()

4.特性(property)

什么是特性

property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

eg:

图片 17图片 18

BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体质指数(BMI)=体重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86

功能描述

图片 19图片 20

class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height

    @property
    def bmi(self):
        return self.weight / (self.height * self.height)

# egon=People('egon',75,1.80)
#
# egon.bmi=egon.weight / (egon.height * egon.height)
# print(egon.bmi)
#
# yl=People('yangli',85,1.74)
# yl.bmi=yl.weight / (yl.height * yl.height)
# print(yl.bmi)


# 首先需要明确。bmi是算出来的,不是一个固定死的值,也就说我们必须编写一个功能,每次调用该功能
#都会立即计算一个值
egon=People('egon',75,1.80)
yl=People('yangli',85,1.74)

# 但很明显人的bmi值听起来更像一个名词而非动词
# print(egon.bmi())
# print(yl.bmi())


# 于是我们需要为bmi这个函数添加装饰器,将其伪装成一个数据属性
# egon.weight=70
# print(egon.bmi) #21.604938271604937,调用egon.bmi本质就是触发函数bmi的执行,从而拿到其返回值
# print(yl.bmi)

示例代码

 

为什么要用特性

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

PS:

【public】
这种其实就是不封装,是对外公开的
【protected】
这种封装方式对外不公开,但对朋友(friend)或者子类(形象的说法是“儿子”,
但我不知道为什么大家 不说“女儿”,就像“parent”本来是“父母”的意思,但中文都是叫“父类”)公开
【private】
这种封装对谁都不公开

python并没有在语法上把它们三个内建到自己的class机制中,可以通过property方法实现

图片 21图片 22

class Foo:
    def __init__(self,val):
        self.__NAME=val #将所有的数据属性都隐藏起来

    @property
    def name(self):
        return self.__NAME #obj.name访问的是self.__NAME(这也是真实值的存放位置)

    @name.setter
    def name(self,value):
        if not isinstance(value,str):  #在设定值之前进行类型检查
            raise TypeError('%s must be str' %value)
        self.__NAME=value #通过类型检查后,将值value存放到真实的位置self.__NAME

    @name.deleter
    def name(self):
        raise TypeError('Can not delete')

f=Foo('egon')
print(f.name)
# f.name=10 #抛出异常'TypeError: 10 must be str'
del f.name #抛出异常'TypeError: Can not delete'

示例代码

 

图片 23图片 24

1.什么是封装

字面上的意思就是,把东西隐藏起来了。

在python中的封装就是把 类中的属性(变量、函数)隐藏起来

View Code

3.怎么应用继承

eg:

class ParentClass1: # 定义父类1
    pass

class ParentClass2: # 定义父类2
    pass

class Subclass1(ParentClass1): # 单继承 父类1
    pass

class Subclass2(ParentClass1,ParentClass2):  # 多继承多个父类  父类1 父类2
    pass

print(Subclass1.__bases__)  # 查看所有父类信息
print(Subclass2.__bases__)  # 查看所有父类信息

结果:以元组的形式返回
(<class '__main__.ParentClass1'>,)
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

 

图片 25图片 26

1.什么继承

继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类。**

继承顺序

一、继承

  抽象只是分析和设计过程中,一个动作或者一种技巧,通过抽象可以得到类

3、如何用多态

动物有多种形态:人,狗,猪

图片 27图片 28

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #动物的形态之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #动物的形态之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #动物的形态之三:猪
    def talk(self):
        print('say aoao')

Animal

文件有多种形态:文本文件,可执行文件

图片 29图片 30

import abc
class File(metaclass=abc.ABCMeta): #同一类事物:文件
    @abc.abstractmethod
    def click(self):
        pass

class Text(File): #文件的形态之一:文本文件
    def click(self):
        print('open file')

class ExeFile(File): #文件的形态之二:可执行文件
    def click(self):
        print('execute file')

File

PS:

1.抽象基类是不能实例化的

2.继承抽象基类的类,必须重写其抽象父类中的抽象方法

抽象类

1、什么是多态

* 多态指的是同一种事物多种形态*

  抽象类:抽象类是一个特殊的类,特殊之处在于只能被继承,不能被实例化。抽象类中只能有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法

3.怎么用封装

在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)

#其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形
#类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式:

class A:
    __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
    def __init__(self):
        self.__X=10 #变形为self._A__X
    def __foo(self): #变形为_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在类内部才可以通过__foo的形式访问到.

#A._A__N是可以访问到的,
#这种,在外部是无法通过__x这个名字访问到。

PS:

*1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N,**即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形,主要用来限制外部的直接访问。


2.变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形

图片 31

3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

 

图片 32图片 33

#正常情况
>>> class A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B


#把fa定义成私有的,即__fa
>>> class A:
...     def __fa(self): #在定义时就变形为_A__fa
...         print('from A')
...     def test(self):
...         self.__fa() #只会与自己所在的类为准,即调用_A__fa
... 
>>> class B(A):
...     def __fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from A

示例代码

 

 

 

#封装数据属性的意义
class People:
    def __init__(self,name,age):
        self.set_info(name,age)

    def tell_info(self):
        print("name:%s---age:%s"%(self.__name,self.__age))

    def set_info(self,name,age):
        if type(name) is not str:
            raise TypeError('name must be str')
        self.__name=name
        self.__age=age

p=People('lary',18)
p.tell_info()

2.为什么要有继承

子类会“”遗传”父类的属性,从而解决代码重用问题,减少代码的冗余

  封装的意义:

二、封装

#特性(property)
class People:
    def __init__(self,name,age,height,weight):
        self.name=name
        self.age=age
        self.height=height
        self.weight=weight

    @property
    def bmi(self):
        return self.weight/(self.height ** 2)

egon=People('egon',18,1.80,75)
print(egon.bmi)

5.属性查找

'''
1、新式类:
    继承object的类,以及该类的子类,都是新式类

    在python3中,如果一个类没有指定继承的父类,默认就继承object
    所以说python3中所有的类都是新式类

2、经典类(只有在python2才区分经典类与新式类):
    没有继承object的类,以及该类的子类,都是经典类
'''

单继承名称空间的查找顺序:

对象自身——–>>当前类——–>>父类——–>>object     
# 查找不到,报错

多继承名称空间的查找顺序:

图片 34

图片 35

在菱形继承的背景下,查找属性
1、经典类:深度优先
2、新式类:广度优先

 

class People:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age

    @property
    def name(self):
        return self.__name

    @name.setter
    def name(self,obj):
        self.__name=obj

    @name.deleter
    def name(self):
        #del self.__name
        raise PermissionError ('no del')

egon=People('egon',28)
print(egon.name)
egon.name='EGON'
print(egon.name)
del egon.name

  继承顺序:如果继承关系为非菱形结构,则会按照先找B这一条分支,然后再找C这一条分支,最后找D这一条分支的顺序直到找到我们想要的属性,如果继承关系为菱形结构,那么属性的查找方式有两种,分别是:深度优先和广度优先

class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

class Student(People):
    def learn(self):
        print('%s is learning'%self.name)

class Teacher(People):
    def teach(self):
        print('%s is teaching'%self.name)

stu1=Student('lary',18)
teacher1=Teacher('egon',20)
stu1.learn()
teacher1.teach()

  组合与继承:通过继承建立了派生类与基类之间的关系,它是一种“是”的关系,当类之间有很多相同的功能,提取这些共同的功能做成基类;组合的方式建立了类与组合的类之间的关系,它是一种“有”的关系,当类之间有明显的不同,并且较小的类是较大的类所需要的组件时,用组合比较好

  属性查找顺序:对象->类->父类->父类的父类。。。

View Code

class Foo:
    def f1(self):
        print('foo f1...')

    def f2(self):
        print('foo f2...')
        self.f1()


class Bar(Foo):
    def f1(self):
        print('bar f1...')

b=Bar()
b.f2()

  当我们创建一个类时,新建的类可以继承一个或多个父类(python支持多继承),父类又可以称为基类或超类,新建的类称为派生类或子类,子类会继承父类的属性,可以减少代码冗余

  继承描述的是子类与父类之间的关系,是一种“什么是什么”(例如:人是动物)的关系。要找出这种关系,必须先抽象再继承。

  继承的实现原理

经典类和新式类

图片 36图片 37

图片 38图片 39

图片 40

class A(object):
    def test(self):
        print('from A')

class B(A):
    def test(self):
        print('from B')

class C(A):
    def test(self):
        print('from C')

class D(B):
    def test(self):
        print('from D')

class E(C):
    def test(self):
        print('from E')

class F(D,E):
    # def test(self):
    #     print('from F')
    pass
f1=F()
f1.test()
print(F.__mro__) #只有新式才有这个属性可以查看线性列表,经典类没有这个属性

#新式类继承顺序:F->D->B->E->C->A
#经典类继承顺序:F->D->B->A->E->C
#python3中统一都是新式类
#pyhon2中才分新式类与经典类

多态性

s=str('hello')
l=list([1,2,3])
t=tuple((4,5,6))

print(len(s))
print(len(l))
print(len(t))

图片 41图片 42

1.继承与派生

#在python中用双下划线开头的方式将属性隐藏起来(设置成私有的)
#__开头的属性只是语法意义的变形,这种变形只在定义时发生一次,这种隐藏对外不对内
class Foo:
    __N=1            #将类的数据属性设置成私有的__N,在定义阶段会自动变形为_Foo__N

    def __init__(self,x,y):
        self.x=x
        self.__y=y

    def f1(self):
        print('f1')

    def f2(self):     #在内部可以访问私有的数据
        print("N:%s---Y:%s"%(self.__N,self.__y))

print(Foo.__dict__)
obj=Foo(1,2)
print(obj.f2())
print(obj.__dict__)
obj.__z=5
print(obj.__dict__)
print(obj.__z)      #变形的过程只在类的定义阶段发生,在定义后的赋值操作不会变形

#在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有
class Parent:
    def __f1(self):
        print('P f1')

    def f2(self):
        print('p f2')
        self.__f1()  #只会与自己所在的类为准,即调用_Parent__f2

class sub(Parent):
    def __f1(self):
        print('s f1')

s=sub()
s.f2()

图片 43图片 44

图片 45图片 46

  封装数据属性的意义:将数据隐藏起来然后对外提供操作该数据的接口,在该接口上对该数据操作进行限制,以完成对数据属性操作的严格控制

class Pig:
    def eat(self):
        print('Pig is eating')

class Dog:
    def eat(self):
        print('Dog is eating')

class Radio:
    def eat(self):
        print('Radio is eating')
#单继承和多继承
class ParentClass1:
    pass

class ParentClass2:
    pass

class subClass1(ParentClass1):  #单继承
    pass

class subClass2(ParentClass1,ParentClass2): #多继承,用逗号分隔开多个继承的类
    pass

#查看继承的父类
print(subClass1.__bases__)
print(subClass2.__base__)   #只查看从左到右继承的第一个子类
print(subClass2.__bases__)  #查看所有继承的父类

View Code

class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def tell_info(self):
        print('name:%s--age:%s'%(self.name,self.age))

class Student(People):
    def learn(self):
        print('%s is learning'%self.name)

    def tell_info(self):
        print('student:',end='')
        print('name:%s--age:%s' % (self.name, self.age))

stu1=Student('lary',18)
stu1.tell_info()

图片 47

图片 48图片 49

View Code

View Code

  封装方法的意义:隔离复杂度(对于使用者来说只需要知道接口的功能,其余功能可以隐藏起来,这样既隔离了复杂度,也提升了安全性)

 2.多态与多态性

重用父类方法的两种方式

#经典类和新式类
print(ParentClass1.__bases__)
#object类:所有python类的基类,提供了一些常见方法的实现(如__str__)
#经典类:没有继承object的类,以及该类的子类
#新式类:无论是否继承object,都默认继承object的类,以及该类的子类
#在python2中,类分为经典类和新式类;在python3中,所有类均为新式类

View Code

图片 50图片 51

  多态指的是同一类事物有多种形态

图片 52图片 53

  特性(property):property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值,这种特性的使用方式遵循了统一访问的原则

 

 3.封装

  子类中调用父类的方法

  组合:指的是在一个类中以另外一个类的对象作为数据属性

# 子类重用父类的两种方法
# 父类名.父类方法()
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def tell_info(self):
        print('name:%s---age:%s'%(self.name,self.age))

class Student(People):
    def __init__(self,name,age,sex):
        People.__init__(self,name,age)
        self.sex=sex

stu1=Student('lary',18,'female')
stu1.tell_info()

#super()
class People:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def tell_info(self):
        print('name:%s---age:%s'%(self.name,self.age))

class Student(People):
    def __init__(self,name,age,sex):
        super().__init__(name,age)  #参照MRO列表检查父类,获取属性
        self.sex=sex

stu1=Student('lary',18,'female')
stu1.tell_info()

  继承原理:对于定义的每一个类,python会计算出一个方法解析顺序列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。为了实现继承,python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配的属性为止

#抽象类
import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod     #定义抽象方法,无需实现功能
    def eat(self):
        pass

class People(Animal):       #子类继承抽象类,但是必须定义抽象类中定义的方法(如eat方法)
    def eat(self):
        print('people is eating')

peo1=People()
peo1.eat()

   派生:子类可以添加自己的新属性,或者重新定义已经在父类中定义过的属性,一旦定义了与父类重名的属性,那么调用该属性时,以子类自己的属性为准