面向对象之继承

一.继承

1.什么是继承

  在程序中继承是一种新建子类的方式,新创建的类称之为子类\派生类,被继承的类称之为父类\基类\超类

  继承描述的一种遗传关系,子类可以重用父类的属性

2.为何用继承?

  减少类与类之间代码冗余的问题

3.如何继承

  先抽象再继承

在Python中继承的特点?单继承&多继承

class Parent1(object):

  pass

class Parent2:

  pass

class Sub1(Parent1):

  pass

class Sub2(Parent2):

  pass

print(Parent1.__bases__)

print(Parent2.__bases__)

python2与python3在继承上的区别

新式类:但凡继承object类的子类,以及该子类的子子类,...都称之为新式类

经典类:没有继承object类的子类,以及该子类的子子类,...都称之为经典类

只有在Python2中才区分新式类与经典类

二.基于继承解决类与类代码冗余问题

在子类派生的新功能中如何重用父类的功能:

方式一:指名道姓地访问某一个类中的函数,与继承无关

class OldboyPeople:
    school = 'Oldboy'
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender
print(OldboyPeople.__init__)

class OldboyPeople(OldboyPeople):
    
    def choose_course(self):
        print('%s is choosing course'%self.name)

class OldboyTeacher(OldboyPeople):
    #            tea, 'egon', 18, 'male', 10, 3000
    def __init__(self, name, age, gender,level,salary):
        # self.name = name
        # self.age = age
        # self.gender = gender
        OldboyPeople.__init__(self, name, age, gender)

        self.level=level
        self.salary=salary

    def score(self,stu,num):
        stu.num=num
        print('老师%s给学生%s打分%s' %(self.name,stu.name,num))

# stu=OldboyStudent('kevin',38,'male') #__init___(stu1,'kevin',38,'male')
# print(stu.__dict__)
tea=OldboyTeacher('egon',18,'male',10,3000) #__init___(tea,'egon',18,'male',10,3000)
print(tea.__dict__)
# print(stu.school)
# print(tea.school)

 

三. 在单继承背景下属性的查找

在单继承背景下,无论是新式类还是经典类属性查找顺序都一样

先obj->父类->...

class Foo:
    def f1(self):
        print('Foo.f1')
        
    def f2(self):
        print('Foo.f2')
        self.f1() #obj.f1()

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

在多继承背景下,如果一个子类继承了多个分支,但是多个分支没有汇聚到一个非常object类,无论是新式类还是经典类查找顺序都一样,会按照从左到右的顺序一个分支一个分支的查找下去

 

在多个继承背景下,如果一个子类继承了多个分支,但是多个分支最终汇聚到一个非object类(菱形继承问题)

新式类:广度优先查找:obj->A->B->E->C->F->D->G->object

经典类:深度优先查找:obj->A->B->E->G->C->F->D

四.super方法

在子类派生出的新功能中如何重用父类的功能:

方式一:指名道姓访问某一个类中的函数,与继承无关

方式二:super(OldboyTeacher,self),在python3中super可以不停传参,调用该函数会得到一个特殊的对象,该对象是专门用来访问父类中属性,

  强调:super会严格参照类的mro列表依次查找属性

#A没有继承B,
class A:
    def test(self):
        print('A.test')
        super().test()
class B:
    def test(self):
        print('from B')
class C(A,B):
    pass

c=C()
c.test()
print(C.mro())

 作业:

1、类的属性和对象的属性有什么区别?

类的属性是类里的对象共有的,对象的属性是对象特有的.
2、面向过程编程与面向对象编程的区别与应用场景?

面向过程可以把复杂的问题拆分,简单化流程化,适用于扩展性要求低的应用场景,如登录界面,面向对象编程,适用于对拓展性要求高的场景,如对战游戏.
3、类和对象在内存中是如何保存的。

类是直接执行代码,将产生的名称存入名称空间,  对象是先产生一个空的对象 然后运行__init__函数 将这个对象以及额外的参数组装后传给该函数,
4、什么是绑定到对象的方法,、如何定义,如何调用,给谁用?有什么特性

类中的功能函数,就是该类中对象的绑定方法,

class 类名:

  def 函数名(self):

    pass

p2.函数名()

给对象用

特殊:在使用绑定方法时,不需要关系self参数 会自动将这个对象本身传进来

对象用绑定方法是 最后执行的还是类中的那个函数

5、如下示例, 请用面向对象的形式优化以下代码
在没有学习类这个概念时,数据与功能是分离的,如下
def exc1(host,port,db,charset):
  conn=connect(host,port,db,charset)
  conn.execute(sql)
  return xxx

def exc2(host,port,db,charset,proc_name)
  conn=connect(host,port,db,charset)
  conn.call_proc(sql)
  return xxx
# 每次调用都需要重复传入一堆参数
exc1('127.0.0.1',3306,'db1','utf8','select * from tb1;')
exc2('127.0.0.1',3306,'db1','utf8','存储过程的名字')

class MySQLHandler:
    def __init__(self,host,port,db,charset='utf8'):
        self.host=host
        self.port=port
        self.db=db
        self.charset=charset
        self.conn=connect(self.host,self.port,self.db,self.charset)
    def exc1(self,sql):
        return self.conn.execute(sql)

    def exc2(self,sql):
        return self.conn.call_proc(sql)


obj=MySQLHandler('127.0.0.1',3306,'db1')
obj.exc1('select * from tb1;')
obj.exc2('存储过程的名字')

 

6、下面这段代码的输出结果将是什么?请解释。
class Parent(object):
  x = 1

class Child1(Parent):
  pass

class Child2(Parent):
  pass

print(Parent.x, Child1.x, Child2.x)
Child1.x = 2
print(Parent.x, Child1.x, Child2.x)
Parent.x = 3
print(Parent.x, Child1.x, Child2.x)

1,1,1

1,2,1

3,2,3

7、多重继承的执行顺序,请解答以下输出结果是什么?并解释。

class A(object):
  def __init__(self):
  print('A')
  super(A, self).__init__()

class B(object):
  def __init__(self):
  print('B')
  super(B, self).__init__()

class C(A):
  def __init__(self):
  print('C')
  super(C, self).__init__()

class D(A):
  def __init__(self):
  print('D')
  super(D, self).__init__()

class E(B, C):
  def __init__(self):
  print('E')
  super(E, self).__init__()

class F(C, B, D):
  def __init__(self):
  print('F')
  super(F, self).__init__()

class G(D, B):
  def __init__(self):
  print('G')
  super(G, self).__init__()

if __name__ == '__main__':
  g = G()
  f = F()

G D A B

F C B D A

8、什么是新式类,什么是经典类,二者有什么区别?什么是深度优先,什么是广度优先?在python2中,没有显式的继承object类的类,以及该类的子类,都是经典类.

在python2中,显式地声明继承object的类,以及该类的子类,都是新式类
在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类
当继承关系为菱形结构时(即一个子类继承了多个分支,且分支最后汇聚到一个非object类时),查找顺序经典类深度优先,新式类广度优先.
深度优先:一条路走到黑,按照从左往右一个分支一个分支的找下去,
广度优先:从左往右,依次运行,当查找到到汇聚的类时,返回上一子类,进入下一个分支进行查找,当汇聚的父类的分支子类全被查找过以后,查找该父类,继续从左往右依次查找.

9、用面向对象的形式编写一个老师类, 老师有特征:编号、姓名、性别、年龄、等级、工资,老师类中有功能
1、生成老师唯一编号的功能,可以用hashlib对当前时间加上老师的所有信息进行校验得到一个hash值来作为老师的编号
def create_id(self):
pass
2、获取老师所有信息
def tell_info(self):
pass

3、将老师对象序列化保存到文件里,文件名即老师的编号,提示功能如下
def save(self):
with open('老师的编号','wb') as f:
pickle.dump(self,f)

4、从文件夹中取出存储老师对象的文件,然后反序列化出老师对象,提示功能如下
def get_obj_by_id(self,id):
return pickle.load(open(id,'rb'))

 

10、按照定义老师的方式,再定义一个学生类

11、抽象老师类与学生类得到父类,用继承的方式减少代码冗余


12、基于面向对象设计一个对战游戏并使用继承优化代码,参考博客
http://www.cnblogs.com/linhaifeng/articles/7340497.html#_label1

 

posted @ 2018-10-23 16:59  Milford  阅读(140)  评论(0编辑  收藏  举报