面向对象与继承

print('面向对象')
'''
今日内容:
1,继承
2,多态
3,封装

4,绑定方法和非绑定方法
classmethod
staticmethod

5,面向对象高级
反射
一些内置方法
__str__
__del__
6,元类(看视频)
这个是属于面向对象底层的知识点,可能会打破你之前对于面向对象的知识点认知
所以这个放到后面讲,当你对面向对象有了足够的了解之后,再深入的研究这个

面向对象:核心是对象二字
对象:对象是特征(变量)与技能(函数)的结合体
基于该思想编写程序,就好比是在创造一个世界,你就是这个世界的上帝
世界是由一个个对象组成的
在上帝眼里所有存在的事物都是对象,任何不存在的事物也可以创造出来

优点:可扩展性强
缺点:编程复杂度高于面向过程编程
类:
对象是特征与技能的结合体
而类则是一系列对象相同特征与技能的结合体
对象是具体存在的,而类是总结出来的抽象的概念

类本质就是一个容器(名称空间)
对象本身也是一个容器(名称空间)

在现实世界中,先有对象,再先有类
在程序中,先定义类,再调用类,实例化生成对象

定义类
class OldboyStudent:
# "文档注释"

#相同的特征
school = 'oldboy'

#相同的技能:
def choose_course(self):
pass

类体代码会在类定义阶段立即执行,产生一个类的名称空间,用来存放类体代码执行过程中产生的名称与变量

使用类
两种用途:
1,当做一个容器
2,调用类产生对象
调用类会产生两件事:
1:产生一个空对象
2,触发类的函数,对象的绑定方法
强调__init__方法:
1,该方法是在产生对象之后才调用
2,该方法可以有任意python代码,但是唯独不能有返回值

属性查找:
强调:类的属性与对象的属性,访问时必须加前缀

先从对象自己的名称空间中查找,如果没有再到类的名称空间中查找

绑定方法:
类中定义的函数,是类的函数属性,就是一个普通函数,没有自动传参的效果
但类中定义的函数其实是绑定给对象用的,称之为绑定方法,绑定方法的特殊之处
就在于绑定给谁就应该由谁来调用,谁来调用就会将第一个位置的参数自动传入

在python 中统一了一个类与类型的概念
一切皆对象

class OldboyStudent:
# "文档注释"
# 相同的特征
school = 'oldboy'
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
pass
# 相同的技能:
def choose_course(self):
pass
# print(OldboyStudent.__dict__)
stu1 = OldboyStudent('szp',24,'male')
print(stu1.name)
print(stu1.age)
print(stu1.gender)
'''
print('hello')


'''
面向对象的三大特性:
1,继承
2,多态
3,封装


一.继承
1,什么是继承?
继承是一种新建类的方式,新建的类称之为子类,被继承的类称之为基类,父类 或者超类
继承描述的是一种遗传的关系,子类可以重用父类的属性

在python中的继承注意两点:
1,在python中支持一个紫烈同时继承多个父类, 而在java中,一个子类只能继承一个父类
2,Python中类分为两种:
新式类:但凡继承object的类,以及该类的紫烈 都是新式类
经典类:没有继承object的类,以及该类的子类 都是经典类
在python3中,一个类如果没有继承任何类,那么默认继承object类,即python3中,所有的类都是新式类
在python2中才区分新式类与经典类


2,为何要用继承?
1,减少代码冗余

3,如何用继承?
class Parent1:
name = 'szp'

class Parent2:
age = 24
pass

class Subclass(Parent1,Parent2):
pass

print(Parent1.__bases__)
print(Subclass.__bases__)

print(Subclass.name)
print(Subclass.age)


'''


'''
1,如何利用继承减少代码的冗余?
2,在继承的背景下,属性查找的优先级
3,新式类与经典类的区别????????????????


现在我们来重点学习一下,如何用继承减少代码的冗余?
继承解决的是类与类之间的代码冗余的问题,一定是一个类是另一个类的子类 存在这种父子关系

总结继承关系:

总结对象之间的相似之处得到类,总结类与类之间的相似之处就得到了类们的父类


问题:如何在子类派生出的新方法中重用父类的功能???

在子类派生的新方法中,重用父类的功能方式一:
知名道姓的访问某一个类的函数
注意:
1,该方式与继承是没有关系的
2,访问的是某一个类的函数,没有自动传值的效果


这种做法是直接调用,就像调用函数一样,和继承没有多大关系,但是很好的解决了这个代码冗余的问题


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

class OldboyStudent(OldboyPeople):
score = 0

def __init__(self,name,age,sex,score):
OldboyPeople.__init__(self,name,age,sex)
self.score = score

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

class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
OldboyPeople.__init__(self,name,age,sex)
self.level = level

def score(self,stu,num): ####老师的打分功能
stu.score = num


stu1 = OldboyStudent('szp',24,'male',90)
print(stu1.name)
print(stu1.age)
print(stu1.sex)
print(stu1.score)

tea = OldboyTeacher('Owen',34,'male',8)
print(tea.name)
print(tea.age)
print(tea.sex)
print(tea.level)




2,在继承的背景下,属性查找的优先级

现在研究这个问题2,在继承的背景下,属性查找的优先级,既然说到继承背景,就需要考虑分成两个方面:一个是单继承,也就是继承一个,
另一个是多继承,也就是一个子类继承多个父类的

我们首先考虑单继承:
在单继承的情况下,属性查找,首先找对象自己的,找不到再去找对象所属的子类,子类再找不到,就去找父类,然后找父类的父类
一直找上去

验证:
class Foo:
x = 1
pass

class Bar(Foo):
# x = 2
pass

obj = Bar()
# obj.x = 3
print(obj.x)


例子2
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')


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



现在我们来研究一下在多继承的情况下,属性查找的优先级与顺序
这个查找顺序是先从左到右 一条支路一条支路的查找,一条支路查找完,再回去到原来的地方,查钊第二条支路

多继承情况下,最好不要将程序的继承关系搞得太复杂,如果继承关系形成了一个闭环,圆圈 或者说是一个多边的棱形,
这样不利于代码的维护,程序的扩展性差,
我们写代码一般要强调解耦合,要将代码的各个级别分开,这样才有利于程序的扩展,
如果我们将所定义的类的继承关系,形成了一个棱形,这样也就是形成了一种强耦合的关系,各部分的连接继承关系十分复杂,
利用类的继承属性,本质是为了减少代码的冗余,但是,简化代码,如果我们这么做了,就有点违背我们的初衷了


一旦出现棱形继承问题,新式类与经典类在属性查找上的区别是:
新式类(继承了object的类以及他的子类):广度优先查找
在最后一个分支查找顶级类(也就是最上面的那个继承了object的那个类)
经典类(没有继承object的类并且父类及以上所有的类都没有继承object类):深度优先查找
在第一个分支就查找顶级类



前面我们介绍了一种在子类派生出的新方法中重用父类的功能的方式,就是指名道姓的调用父类中函数的功能,这种做法不能自动将调用者给
函数传值,现在,我们介绍另外一种方法

子类派生出的新方法中重用父类的功能方式二:
##就是通过调用super()这个函数
注意:
在python2 中,super(自己的类名,对象自己self)
在python3中,super() 括号内的参数可以省略
调用super() 会得到一个返回值,该返回值就是一个特殊的对象,该特殊的对象是专门用来引用父类中的属性
1,只能在子类中用
2,该方式严格依赖于继承
3,访问是绑定方法,有自动传值的效果

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

class OldboyStudent(OldboyPeople):
score = 0

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

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

class OldboyTeacher(OldboyPeople):
def __init__(self,name,age,sex,level):
OldboyPeople.__init__(self,name,age,sex)
self.level = level

def score(self,stu,num): ####老师的打分功能
stu.score = num

stu1 = OldboyStudent('szp',24,'male',90)
print(stu1.sex)
print(stu1.name)
print(stu1.sex)

现在为止,我们一共介绍了两种,派生的子类中重用父类中功能的方法.一种是指名道姓的调用函数,没有自动传值的功能,
另外一种是借助super() 函数的功能,这个有自动传值的功能,
两种方式没有优劣之分,但是你最好使用一种,不要两种混合使用,但是当你看到另外一种的代码的时候,也要能看懂
'''
posted @ 2019-05-22 14:56  同济小孙  阅读(147)  评论(0编辑  收藏  举报