25.面向对象(抽象类. 多态)
2019-12-28 本文已影响0人
哈哈大圣
面向对象(抽象类. 多态)
一. 抽象类
- 抽象类不能实例化
- 继承抽象类的子类必须将抽象类中所有的抽象方法重写
- 抽象类实现方式 (个人总觉得很别扭,估计是写Java写习惯了吧 @-@ )
import abc # 抽象类专用模块
class Animal(metaclass = abc.ABCMeta): #定义一个抽象类,相当于java中interface接口,也叫抽象类,指定的元类为abc.ABCMeta
attributes = "Animal"
# 申明抽象方法
@abc.abstractmethod
def run(self): # 抽象方法里面不写逻辑代码
pass
@abc.abstractmethod
def eat(self):
pass
class Person(Animal):
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
def run(self):
print("%s is runing"%self.name)
def eat(self):
print("%s is eating"%self.name)
xiaoming = Person("xiaoming",21,"male")
xiaoming.run()
二. 多态
1). Python中多态的特点
-
在不考虑对象的类型的情况下而直接使用对象的方法属性
- 父类用子类实例化的类;
- 鸭子类型,非继承的关系,但是有同样的函数签名;
-
动态多态性:调用方法的时候,不考虑继承关系
-
静态多态性:比如
+
号的多态性:2 + 3
"mingtia" + "houtin"
[1,2,3] + [4,5,6]
-
好处:增加了程序的灵活性;增加了程序的扩展性
-
静态多态就是在系统编译期间就可以确定程序执行到这里将要执行哪个函数
-
动态多态则是利用虚函数实现了运行时的多态,也就是说在系统编译的时候并不知道程序将要调用哪一个函数,只有在运行到这里的时候才能确定接下来会跳转到哪一个函数的栈帧。
2). 多态的使用
- 继承关系
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def talk(self):
pass
class People(Animal):
def talk(self):
print('say hello')
class Cat(Animal):
def talk(self):
print('say miamiao')
def func(animal):
# 不考虑对象的类型,把它当做想要的类型
animal.talk()
func(People())
func(Cat())
- 鸭子类型
class Disk:
def read(self):
print('disk read')
def write(self):
print('disk write')
class Text:
def read(self):
print('text read')
def write(self):
print('text write')
def file_read(obj):
return obj.read()
file_read(Disk())
file_read(Text())
三. 封装
1). Python的封装 (伪)
- 要隐藏只能在类定义的时候或对象初始化的时候进行定义隐藏属性:类的数据属性,init方法里面的属性
- 类定义后通过
类.__var
这种类型不是属于隐藏属性; 必须在定义时才有效
- 类定义后通过
- Python的封装,将数据属性对外不可见的措施是定义名字的时候 Python解释器在变量前面加杠杠,如
__name
2). 特点:
- 在类外部无法直接
obj.__AttrName
- 外部强行访问的方式,在前面再加上
_A
, 例如_A__name
- 外部强行访问的方式,在前面再加上
- 在类内部是可以直接使用:
obj.__AttrName
- 子类无法覆盖父类
__
开头的属性
3). 多态的使用
- 隐藏属性特性
- 在定义时生效
- 内部函数生成生效
- 定义后外部通过
.
添加无效 - 子类无法访问父类的隐藏属性
- 定义与父类同名的隐藏属性不会相互干扰
class A:
# __dict__中 为 _A__x ,隐藏属性定义时发生,
# 当定义结束后再添加的__开头的也只能是普通的属性
__x = "A"
def __init__(self, name):
# 隐藏属性
self.__name = name
def abr(self):
print(self.__x)
# 外部调用次此方法,也是生成隐藏的
def set(self):
self.__new = "new"
class B(A):
def tall(self):
# 就算是继承的,也找不到父类中隐藏的属性,只能找自己的
# print("from B %s" % self.__x)
pass
class C(A):
# 这个是C自己的隐藏属性,并没有覆盖父类的隐藏属性!
__x = "C"
a = A("a")
# 在类定义或者对象生成之后定义的,只是普通属性,并非隐藏属性
A.__y = "Y"
# 在类定义或者对象生成之后定义的,只是普通属性,并非隐藏属性
a.__y = "Y"
# 打印变量空间信息
print(A.__dict__)
# 在类的内部可以访问
a.abr()
# 无法直接访问到隐藏的属性
print(a.__x)
- 隐藏属性的继承查找方式
- 不同于一般属性,隐藏属性查找规则,只能在所在类中进行查找!不会从发起调用方的子类或者父类中找。
class A:
def f1(self):
print("f1 from A")
def __f1(self):
print("__f1 from A")
def f2(self):
self.f1()
self.__f1() # 顺着继承链(从MRO头开始找)找的就是 _A__f1
class B(A):
def __f1(self):
print("__f1 for, B")
def f1(self):
print("f1 from B")
b = B()
b.f2()
# f1 from B
# __f1 from A
4). 封装的意义
- 封装不是单独意义上的隐藏,隔离复杂度
- 封装数据属性:明确的区分内外,控住外部对隐藏属性的操作行为
class People:
def __init__(self,name,age):
self.__name = name
self.__age = age
def tell_info(self):
print("Name:<%s> Age:<%s>" % (self.__name,self.__age))
# 相当于java中的set方法,不能直接访问对象的属性,但是提供一个修改的接口!
def set_info(self,name,age):
# 判断是否为类的实例
if not isinstance(name,str):
return print("名字必须是字符串!")
if not isinstance(age,int):
return print("年龄必须是数字!")
self.__name = name
self.__age = age
p = People("Egon",23)
p.set_info("xiaoqing",21)
p.tell_info()
- 封装方法:为了隔离复杂度
class ATM:
def __card(self):
print("插卡")
def __auth(self):
print("用户认证")
# 将内部方法进行封装,隔离复杂度
def withdraw(self):
self.__card()
self.__auth()
a = ATM()
a.withdraw()
- 封装与可扩展性
class Room:
def __init__(self,name,woner,length,wide,hight):
self.name = name
self.woner = woner
self.__length = length
self.__wide = wide
self.__hight = hight
# 修改封装的方法,而调用部分可以不动,实现了扩展性
def get_area(self):
return self.__length * self.__wide * self.__hight
r = Room("卫生间","Egon",10,10,5)
print(r.get_area())