【Python】面向对象封装、继承、多态
封装
1、封装的本质就是隐藏,将一些复杂的执行过程隐藏起来,留下调用的接口(接口就是函数,成为接口函数;一组接口函数的集合体构成一个接口),通过这些接口进行交互。
2、数据封装的主要原因有:保护隐私
3、方法封装的主要原因有:隔离复杂度
4、封装分为两个层面:
第一层面的封装:创建类或者对象,通过类名.
或者对象名.
的方式调用对应的方法,类名.
和对象名.
就是访问隐藏属性的接口
第二层面的封装:类中把某些属性和方法隐藏起来(即定义为私有),只在类的内部使用,外部无法访问,或者留少量的接口(函数)供外部访问。
- 第一层封装
使用构造方法将内容封装到对象中,通过对象直接或者self间接调用被封装的内容
# coding=utf-8
class A:
def __init__(self, name, age):
self.name = name
self.age = age
def Aname(self):
print "名字是:%s" %(self.name)
def Aage(self):
print "年龄是:%d" %(self.age)
a = A('Alice', 10)
a.Aname()
a.Aage()
程序输出:
名字是:Alice
年龄是:10
- 第二层封装
在Python中使用双下划线的方式实现隐藏属性(私有)。类中所有双下划线开头的名称如:__x
都会自动变成_类名.__x
的形式
1.示例:将一个类定义如下
class A:
__x = 1
def __test(self):
print 'private test'
(1)查看A的属性
print A.__dict__
#程序输出:
{'__module__': '__main__', '__doc__': None, '_A__test': <function __test at 0x00000000032BE128>, '_A__x': 1}
(2)调用__x
直接调用失败
print A.__x
#输出:类中没有`__x`,`__x`为私有的,不可以调用
Traceback (most recent call last):
File "*******", line 6, in <module>
print A.__x
AttributeError: class A has no attribute '__x'
通过A._A__x
的形式调用
print A._A__x
#程序输出:
#得到正确输出1
1
(3)实例化A,并调用__x
和__test
a = A() #实例化
print a.__dict__
print a._A__x #对象的调用
a._A__test() #强制访问,对象的调用
#程序输出:
{}
1
private test
2.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的
# 1、正常情况下:
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() #b.test在父类A中找到了test(),得到b.fa()。然后在对象b中找到了B类中的fa方法,输出from B
# 执行结果:
from B
# 注意:时刻谨记:在定义阶段就会变形
# 2、把fa定义成私有的,即__fa
class A:
def __fa(self): #_A__fa
print('from A')
def test(self): #在定义阶段就会变形
self.__fa() #self._A__fa
class B(A):
def __fa(self): #_B__fa
print('from B')
b = B() #实例化
b.test() #自己的对象和类名称空间没有,在父类的名称空间找到,函数已经变形。
#执行结果:
from A
继承
新式类和旧式类
python在2.2版本中引入了descriptor功能,也正是基于这个功能实现了新式类(new-styel class)的对象模型,
同时解决了之前版本中经典类(classic class)系统中出现的多重继承中的MRO(Method Resolution Order)的问题,同时引入了一些新的概念,比如classmethod, staticmethod, super,Property等,这些新功能都是基于descriptor
而实现的。
- 创建方法
新式类是在创建的时候继承内置object对象(或者是从内置类型,如list,dict等),而经典类是直接声明的。
新式类:
class A(object):
pass
旧式类:
class A():
pass
- 继承
新式类在查找时的策略为广度优先
旧式类在查找时的策略为深度优先
可以利用 类名.__mro__
的方法查看类之间的继承关系(旧式类没有这种方法)
新式类继承
class A(object):
def foo1(self):
print "A"
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print "C"
class D(B, C):
pass
d = D()
d.foo1()
print D.__mro__
#程序输出:
#广度优先:D-->B-->C(找到fool()-->输出C)
C
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
旧式类继承
class A():
def foo1(self):
print "A"
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print "C"
class D(B, C):
pass
d = D()
d.foo1()
#程序输出
#深度优先:D-->B-->A(找到fool())-->输出A
A
多态
定义
- 多态(从定义角度出发):同一类事物的多种形态。例如:动物的多种形态–>狗、猫、猪
- 多态性(从使用角度出发):同一种调用方式,不同的执行效果。具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
- 多态性依赖于:继承和定义的接口。
- 多态性的实质:定义了一个函数接口,在这个函数中定义了所有类内通性的功能,只要传入参数(对象名)函数调用执行,就得到不同的结果。这就是所谓的一种调用方式,不同的执行结果。
# coding=utf-8
class Animal:
def run(self):
raise AttributeError('子类必须实现这个方法')
class Dog(Animal):
def run(self):
print 'dog is running'
class Cat(Animal):
def run(self):
print 'cat is running'
class Pig(Animal):
def run(self):
print 'pig is running'
dog = Dog()
cat = Cat()
pig = Pig()
#定义统一的接口
def func(obj): #obj这个参数没有类型限制,可以传入不同类型的值
obj.run() #调用的逻辑都一样,执行的结果却不一样
func(dog)
func(cat)
func(pig)
#程序运行结果:
dog is running
cat is running
pig is running
- 多态的好处
- 增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用 - 增加了程序的扩展性
通过继承父类创建了一个新的子类,使用者无需更改自己的代码,还是用定义的接口函数去调用。
- 增加了程序的灵活性