Python 面向对象编程—封装
继承的概念
继承在生活中,指的是子女继承父辈的财产
Python中 即子类默认继承父类中的所有属性和方法,是描述的类与类之间所属关系.
注
父类的私有权限属性和方法也是可以继承的,区别是调用时操作不同
单继承
一个类只继承一个父类
基本语法:
class 类B(类A):
pass
称为类 B 继承类 A
特点: B类的对象可以使用 A类的属性和方法
优点: 代码复用.重复相同的代码不用多次书写.
名词:
类A: 父类 基类
类B: 子类 派生类
例:
class A:
def __init__(self):
self.name = '小明'
def show(self):
print(self.name)
class B(A):
pass
b = B()
b.show() # 小明
多继承
一个类同时继承了多个父类
基本语法:
class 类B(类A,类C,类D...):
pass
称为类 B 继承类 A,C,D...
特点: B类的对象可以使用 A,C,D...类的属性和方法
例:
class T(object):
def __init__(self):
self.zuchuan = '祖传秘方'
def make_zuchuan(self):
print(self.zuchuan)
class School(object):
def __init__(self):
self.kexue = '科学'
def make_kexue(self):
print(self.kexue)
class B(School,T):
pass
b = B()
b.make_kexue()
b.make_zuchuan()
# 输出:科学
# AttributeError: 'B' object has no attribute 'zuchuan'
当一个类有多个父类的时候,默认使用第一个父类的同名属性和方法
本例中b对象应当含有的内容为,School和T类的属性和方法,但是由于上面原因,有两个相同的init方法,所以只调用第一个父类的方法的init方法,导致b对象只含有School类的make_kexue方法,kexue属性,以及T的make_zuchuan方法,但是没有T的init方法,所以也就没有zuchuan属性,这是本例的错误原因
修改
知道错误原因为init同名,则可以将T中的self.zuchuan定义为类属性,舍去init方法 注 有很多方法解决这个问题,这只是其中一种
class T(object):
def __init__(self):
self.zuchuan = '祖传秘方'
def make_zuchuan(self):
print(self.zuchuan)
class School(object):
kexue = '科学'
def make_kexue(self):
print(self.kexue)
class B(School,T): # 多层继承的格式
pass
b = B()
b.make_kexue()
b.make_zuchuan()
# 输出 科学
# 祖传秘方
注 一定要牢记该多继承的性质
子类重写 调用父类同名属性和方法
为了更好的发挥继承的优势,可以对子类重写和调用父类同名属性和方法,也可以在子类中添加子类独有的方法和属性
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def show(self):
print("我叫%s,今年%d"%(self.name,self.age))
class Teacher(Person): # 继承Perosn,所以Teacher类中以及有了从Person继承来的__init__方法
def show(self): # 这是对Person类进行重写,
print("我叫%s,今年%d,我是一名老师" % (self.name, self.age))
t = Teacher("张三",23)
t.show()
# 输出 我叫张三,今年23,我是一名老师
思考
,如何在多继承中实现调用不同父类同名属性和方法呢?
解
C++中是通过作用域规范,进行调用不同父类相同的方法
Python中则是通过在子类中对父类进行重写方法进行调用
class Master(object):
def __init__(self):
self.zu = '祖传秘籍'
def make_cake(self):
print(self.zu)
class School(object):
def __init__(self):
self.kexue = '科学'
def make_cake(self):
print(self.kexue)
class T(School,Master):
def __init__(self):
self.T = '独创'
def make_cake(self):
print(self.T)
def make_new_Master(self):
Master.__init__(self) #1 由于子类已经重写了init方法,覆盖了父类继承而来的init方法,所以想要调用父类的同名函数,则需要手动实现父类的init函数
Master.make_cake(self)
def make_new_School(self):
School.__init__(self) #2
School.make_cake(self)
if __name__ == '__main__':
t = T()
t.make_cake()
t.make_new_Master()
t.make_new_School()
# 输出 独创 祖传秘籍 科学
思考
如果子类不重写init和make_cake方法,#1和#2处是否都要写?
答
不需要都写,因为当一个类有多个父类的时候,默认使用第一个父类的同名属性和方法,可以动手试一试,但是建议都写,增加规范性,提升代码的健壮性
self的添加
-
在类封装的方法内部,
self
就表示 当前调用方法的对象自己 -
调用方法时,程序员不需要传递
self
参数 -
在方法内部
- 可以通过
self.
访问对象的属性 - 也可以通过
self.
调用其他的对象方法
- 可以通过
-
在类继承中,则需要明白,
self
在方法的括号中:如Master.make_cake(self)
,是为了时刻跟踪从到底是哪一个对象调用了该方法,上例中的#1和#2所属的代码块中的self都不可省略
多层继承
C类继承B类,B类继承A类,则将这种超过两次继承的类的关系称为多层继承
要理解多层继承和多继承的区别
- 多继承是针对单继承的,表明一个类有多个父类
- 多层继承是针对继承次数的,表明一个类至少是子类的子类
class Master(object):
def __init__(self):
self.zu = '祖传秘籍'
def make_cake(self):
print(self.zu)
class School(object):
def __init__(self):
self.kexue = '科学'
def make_cake(self):
print(self.kexue)
class T(School,Master): # 这里杂糅了多继承和多层继承,这里是多继承
def __init__(self):
self.T = '独创'
def make_cake(self):
print(self.T)
def make_new_Master(self):
Master.__init__(self)
Master.make_cake(self)
def make_new_School(self):
School.__init__(self)
School.make_cake(self)
class A(T): # 多层继承关系
pass
if __name__ == '__main__':
a = A()
a.make_cake()
a.make_new_Master()
a.make_new_School()
super()
子类中调用父类的方法:
- 法1:可以用父类类名.方法名
- 法2:用super(类名,self).方法名
举例
在多继承关系中,如何实现在孙类中调用父类和祖父类的同名方法呢?
例子:
class A(object):
def __init__(self):
self.name = '祖父'
def show(self):
print(self.name)
class B(A):
def __init__(self):
self.name = '父亲'
def show(self):
print(self.name)
class C(B):
def __init__(self):
self.name = '孙子'
def show(self):
print(self.name)
if __name__ == '__main__':
c = C()
c.show() # 孙子
方法1:
添加函数
def new_show(self):
B.__init__(self)
B.show(self)
A.__init__(self)
A.show(self)
if __name__ == '__main__':
c = C()
c.show()
c.new_show() # 输出 孙子 父亲 祖父
思考
:当类过多时,这样写是否会十冗余?
解
因此引入super()方法
方法2:
在孙子类中添加super方法,在父亲类中也添加super,则有
class A(object):
def __init__(self):
self.name = '祖父'
def show(self):
print(self.name)
class B(A):
def __init__(self):
self.name = '父亲'
def show(self):
print(self.name)
super(B,self).__init__()
super(B, self).show()
class C(B):
def __init__(self):
self.name = '孙子'
def show(self):
print(self.name)
def new_show(self):
super().__init__()
super().show()
# def new_show(self):
# B.__init__(self)
# B.show(self)
# A.__init__(self)
# A.show(self)
if __name__ == '__main__':
c = C()
c.show()
c.new_show() # 输出 孙子 父亲 祖父
super的本质就是代替该类的父类,当父类的名字改变时,则不需要像方法1那样修改父类名称,增加了代码的健壮性,同时注意这是在孙子类和父亲类中都添加了super方法,需要好好理解为什么这样做,有利于加深理解
注
是否会产生这样的疑问,为什么这里没有传入self参数呢?
解
super有两种形式,一种是例子中的简化模式,一种是super(本类名,self).方法名,所以简化版的super可以不用写self参数,由函数自己追踪,将例子中的super().都改成super(父类名,self). 也是可以正常运行的,可以自己动手试一试
私有属性和私有方法
私有属性和私有方法,就是为类的属性和方法设置为私有权限
私有权限:
定义: 在方法和属性前加上两个下划线, 就变为私有.
性质:
1. 不能在类外部通过对象直接访问和使用, 只能在类内部访问和使用
2. 能被子类继承,但是不能被子类和子类对象使用
公有: 不是私有的,就是公有的.
举例:性质1
class A(object):
def __init__(self):
self.__money = 1000
self.name = '小明'
def __jueji(self):
print("这是我的独门绝技!")
class B(A):
pass
def show(self):
print("我叫%s,有%d元"%(self.name,self.__money))
def show_jueji(self):
self.__jueji()
a = A()
print(a.name) # 小明
print(a,__money) # NameError: name '__money' is not defined
a.__jueji() # AttributeError: 'A' object has no attribute '__jueji'
举例:性质2
class A(object):
def __init__(self):
self.__money = 1000
self.name = '小明'
def __jueji(self):
print("这是我的独门绝技!")
class B(A):
pass
b = B()
print(b.name) # 小明
print(b.__money) # 'B' object has no attribute '__money'
b.__jueji() # 'B' object has no attribute '__jueji'
那如何去使用这些私有属性和方法呢?
可以通过定义外部接口get,set方法进行调用