day25 在继承的背景下属性查找的顺序、组合、多态与接口、鸭子类型

回顾

属性的存放位置:
        类中应该存储所有对象公共的内容
        对象中存储都是每个对象独有的(都不同)
初始化函数:__init__
        给对象的属性赋初值 , 可以保证只要对象被创建就一定有相应的属性
       节省重复代码( 指初始化函数init给每个对象都会初始一份相应的属性)

绑定方法指的是 :将类或对象与函数进行了绑定
        之所以绑定是为了提高整合度,后续在拿到对象就可以直接调用对象相关的所有属性,而无需关心数据是什么 如何处理
        类、对象其实都可以看作是一个存储数据的容器(有属性有方法)

对象绑定方法:

         默认就是绑定给对象的
         当使用对象调用时 会自动传入对象本身作为第一个参数,执行该方法时,直接问self对应的这个对象要数据就可以了
         用类来调用时就是一个普通函数 该怎么传就这么传
类绑定方法
         当执行该方法时,需要访问类中的内容而不需要对象中的内容时用类绑定方法
          @classmethod
         无论使用类还是对象来调用都会自动传入类本身作为第一个参数
非绑定方法
          既不需要访问类中的内容 也不需要访问对象中的内容 那就作为非绑定方法 就是一个普通函数 没有自动传值的效果
         @staticmethod

继承:说的是类与类之间的关系:【什么是什么的关系 如:猫是动物,猫继承了动物类】

           一个类继承另外一外类,继承的类称之为子类、派生类,被继承的类称之为父类、基类或超类

           一个类可以继承多个类。
            继承后,子类可以复用父类的内容 ,减少了类与类之间的代码冗余

在继承背景下属性的查找顺序
         对象本身 -> 所在的类 -> 类的父类 -> .... object

抽象类  指的是 把一系列类中的相同的特征和行为抽取 形成一个新的类 (公共父类)
          使用继承时 应该先抽象 再继承

派生:  某子类拥有与父类不同的内容,优先使用自己的

覆盖:子类出现了与父类完全相同的名字

一切皆对象:在py3里面所有数据全是对象 包括 int list 模块 函数 .....包等等....

在子类派生的新方法中重用父类的功能的两种方式:
       1.指名道姓地引用某一个类中的函数,与继承无关

       2. super() 得到一个对象,该对象专门用来访问父类中的属性,而父类参照mro列表顺寻查找父类
       py3:super()              
       py2:  super(自己的类名,自己的对象self)

今日内容  

一、属性查找顺序:

--  在单继承背景下的属性查找的优先级:先在自己类中查找,没有就去父类中查找

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

def f2(self):
print('Foo.f2')
self.f1() => 此self == obj

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

obj = Bar()
obj.f2()
#Foo.f2
#Bar.f1

-- 在多背景下属性的查找顺序:

  1)如果一个子类继承多个分支,多个分支没有共同继承一个非object的类】,此时属性的查批优先级是

      对象-->对象的类--> 对象的父类-->该分支结束后再返回第二个父类,一个分支一个分支的找下去,最后找到object类,没有就报错,如下图先走B分支,走到G没有就找C 分支,最后找到Object类。新

     此种没有新式类与经典类之分,py3中默认G中继承了object类,py2中没写就真的就是没有,最后也不会找到object类。

2)菱形继承背景下,属性查找的优先级:菱形指的是一个类有多个分支继承,最后会继承到一个公共的类。

              python中的两种类【新式类、经典类】

              新式类:直接或间接继承了object的类. ---    python3中都是新式类,因为默认继承ogject

                            从左往右,从第一个分支找,找到object的子类,再回到第二个分支,同样找到object的子类,依此类推,在最后一个分支找到顶级类,object类

                            查找其他路线(基于C3算法),A.mro()   => 得到一个属性查找顺序的列表

             经典类:没有【直接或间接】继承object的类,都称之为经典类, python2不会默认继承object类,所有只有py2中才有经典类,而且没有继承object类

                           从左往右,从第一个分支找,一条线找到底,没有就从第二个分支找。

二、组合:

1. 什么组合? 组合指的是某一个对象拥有一个属性,该属性的值是另外一个类的对象

2. 为何要用组合?通过为某一个对象添加属性【属性的值是另外一个类的对象】的方式,可以间接地将两个类关联/整合/组合到一起,从而减少类与类之间代码冗余

class Foo1:
pass

class Foo2:
pass

class Foo3:
pass

class Bar():
x = 123

obj_from_bar = Bar()
obj1 = Foo1()
obj2 = Foo2()
obj3 = Foo3()

obj1.attr1 = obj_from_bar
obj2.attr1 = obj_from_bar
obj3.attr1 = obj_from_bar

obj1.attr1.x
obj2.attr1.x
obj3.attr1.x

3. 什么时候用组合?

    让学生/老师有选课的功能,新增一个课程类,如下:

class OldboyPeople:
school = 'oldboy'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


class OldboyStudent(OldboyPeople):
school = 'oldboy'

def __init__(self, name, age, sex, score=0):
# OldboyPeople.__init__(self,name, age,sex)
super(OldboyStudent,self).__init__(name, age, sex)
self.score = score

def choose_coure(self):
print('%s choosing coures' % self.name)


class OldboyTeacher(OldboyPeople):
school = 'Oldboy'

def __init__(self, name, age, sex, level):
super().__init__(name,age, sex)
# OldboyPeople.__init__(self,name,age, sex)
self.level = level

def score(self, stu, num):
stu.score = num


class Course:
def __init__(self,c_name, c_price, c_period):
self.c_name = c_name
self.c_price = c_price
self.c_period = c_period


python = Course('python', 1900, '5month')

stu1 =OldboyStudent('fanny',17, 'female')
stu1.course = python
print(stu1.course.c_name)
print(stu1.course.c_price)
print(stu1.course.c_period)

tea1 = OldboyTeacher('egon', 18, 'male',100)
tea1.course = python
print(tea1.course.c_name)
print(tea1.course.c_price)
print(tea1.course.c_period)

 问题:是否可以减少代码冗余问题?将冗余部分放到课程类中

class OldboyPeople:
school = 'oldboy'
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex


class OldboyStudent(OldboyPeople):
school = 'oldboy'

def __init__(self, name, age, sex, score=0):
# OldboyPeople.__init__(self,name, age,sex)
super(OldboyStudent,self).__init__(name, age, sex)
self.score = score

def choose_coure(self):
print('%s choosing coures' % self.name)


class OldboyTeacher(OldboyPeople):
school = 'Oldboy'

def __init__(self, name, age, sex, level):
super().__init__(name,age, sex)
# OldboyPeople.__init__(self,name,age, sex)
self.level = level

def score(self, stu, num):
stu.score = num


class Course:
def __init__(self,c_name, c_price, c_period):
self.c_name = c_name
self.c_price = c_price
self.c_period = c_period

def tel_info(self):
print('<课程名称:%s 价钱:%s 周期:%s>'% (self.c_name,self.c_price, self.c_period))


python = Course('python', 1900, '5month')

stu1 =OldboyStudent('fanny',17, 'female')
stu1.course = python
stu1.course.tel_info()

tea1 = OldboyTeacher('egon', 18, 'male',100)
tea1.course = python
tea1.course.tel_info()

三、多态与接口、鸭子类型

--  什么是多态:指的是同一种/类事物的不同形态,但不同形态需遵循一个共同的规范【需跟父类有相同名字的方法】

--   为何要用多态:

     提供了一个接口,父类就是一个规范,外界只要知道父类的有什么功能就可以了,

     参照父类中的【定义的函数名跟在父类中有就行了】方法使用,而不需要关心对象内部以及对象所在的类有什么。如len()方法,随便给一个对象就可以统计长度

--   如何用多态: 要想使用接口的便利,必须如下这样:

     1)父类Animal只是用来建立规范的,不能用来实例化的,更无需实现内部的方法

     2) 子类中必须依照定义跟父类相同名字的方法【具体功能自定】

     3) 为了强制让子类完全依照父类,可以给父类加装饰器 。一旦没有按照父类定义方法,就子类就不能实例对象

     4)python中更推崇约定即【鸭子类型】,大家统一遵照就可以了,这样的好外是:可以不需要继承父类,即没有莲藕关系。

     5)鸭子类型:通过谚语来阐述:具体的功能不一样,但是功能名必须一样:例【鸭子类型之读写件】

import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def speak(self):
pass

class People(Animal):
def speak(self):
print('say:hi')

class Dog(Animal):
def speak(self):
print('汪汪')

class Pig(Animal):
def speak(self):
print('哼哼')

obj1 = People()
obj2 = Dog()
obj3 = Pig()

例【鸭子类型之读写件】:

class Disk:
def read(self):
print('Disk read')

def write(self):
print('Disk write')


class Cpu:
def read(self):
print('Cpu read')

def write(self):
print('Cpu write')


class Memory:
def read(self):
print('Memory read')

def write(self):
print('Memory write')

obj1 = Disk()
obj2 = Memory()
obj3 = Cpu()

obj1.read()
obj2.read()
obj3.read()
原文地址:https://www.cnblogs.com/qingqinxu/p/10884027.html