day21 面向对象高级
01,多态与多态性
多态指的是一类事物有多种形态,比如动物有多种形态:猫、狗、猪
class Animal: #同一类事物:动物
def talk(self):
pass
class Cat(Animal): #动物的形态之一:猫
def talk(self):
print('喵喵喵')
class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('汪汪汪')
class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('哼哼哼')
#实例化得到三个对象
>>> cat=Cat()
>>> dog=Dog()
>>> pig=Pig()
多态性指的是可以在不用考虑对象具体类型的情况下而直接使用对象,这就需要在设计时,把对象的使用方法统一成一种:例如cat、dog、pig都是动物,但凡是动物肯定有talk方法,于是我们可以不用考虑它们三者的具体是什么类型的动物,而直接使用
>>> cat.talk()
喵喵喵
>>> dog.talk()
汪汪汪
>>> pig.talk()
哼哼哼
更进一步,我们可以定义一个统一的接口来使用
>>> def Talk(animal):
... animal.talk()
...
>>> Talk(cat)
喵喵喵
>>> Talk(dog)
汪汪汪
>>> Talk(pig)
哼哼哼
Python中一切皆对象,本身就支持多态性
# 我们可以在不考虑三者类型的情况下直接使用统计三个对象的长度
s.__len__()
l.__len__()
t.__len__()
# Python内置了一个统一的接口
len(s)
len(l)
len(t)
多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承Animal类创建了一个新的类,实例化得到的对象obj,可以使用相同的方式使用obj.talk()
... def talk(self):
... print('嗷...')
...
>>> wolf=Wolf() # 实例出一头狼
>>> wolf.talk() # 使用者根本无需关心wolf是什么类型而调用talk
嗷...
综上我们得知,多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象,可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
def talk(self): # 抽象方法中无需实现具体的功能
pass
class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
def talk(self):
pass
cat=Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
但其实我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的“鸭子类型”(duck typing):“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度,如下
#二者看起来都像文件,因而就可以当文件一样去用,然而它们并没有直接的关系
class Txt: #Txt类有两个与文件类型同名的方法,即read和write
def read(self):
pass
def write(self):
pass
class Disk: #Disk类也有两个与文件类型同名的方法:read和write
def read(self):
pass
def write(self):
pass
02,反射
python是动态语言,而反射(reflection)机制被视为动态语言的关键。
反射机制指的是在程序的运行状态中
对于任意一个类,都可以知道这个类的所有属性和方法;
对于任意一个对象,都能够调用他的任意方法和属性。
这种动态获取程序信息以及动态调用对象的功能称为反射机制。
在python中实现反射非常简单,在程序运行过程中,如果我们获取一个不知道存有何种属性的对象,若想操作其内部属性,可以先通过内置函数dir来获取任意一个类或者对象的属性列表,列表中全为字符串格式
>>> class People:
... def __init__(self,name,age,gender):
... self.name=name
... self.age=age
... self.gender=gender
...
>>> obj=People('egon',18,'male')
>>> dir(obj) # 列表中查看到的属性全为字符串
[......,'age', 'gender', 'name']
接下来就是想办法通过字符串来操作对象的属性了,这就涉及到内置函数hasattr、getattr、setattr、delattr的使用了(Python中一切皆对象,类和对象都可以被这四个函数操作,用法一样)
class Teacher:
def __init__(self,full_name):
self.full_name =full_name
t=Teacher('Egon Lin')
# hasattr(object,'name')
hasattr(t,'full_name') # 按字符串'full_name'判断有无属性t.full_name
# getattr(object, 'name', default=None)
getattr(t,'full_name',None) # 等同于t.full_name,不存在该属性则返回默认值None
# setattr(x, 'y', v)
setattr(t,'age',18) # 等同于t.age=18
# delattr(x, 'y')
delattr(t,'age') # 等同于del t.age
基于反射可以十分灵活地操作对象的属性,比如将用户交互的结果反射到具体的功能执行
>>> class FtpServer:
... def serve_forever(self):
... while True:
... inp=input('input your cmd>>: ').strip()
... cmd,file=inp.split()
... if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
... func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
... func(file)
... def get(self,file):
... print('Downloading %s...' %file)
... def put(self,file):
... print('Uploading %s...' %file)
...
>>> server=FtpServer()
>>> server.serve_forever()
input your cmd>>: get a.txt
Downloading a.txt...
input your cmd>>: put a.txt
Uploading a.txt...
03,内置方法
1,什么是内置方法
定义在类内部,以__开头并以__结束的方法
特点:会在某种情况下自动触发执行
2,使用内置方法的原因
为了定制化我们的类或对象
3,内置方法的使用
class People:
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return "<%s:%s>" %(self.name,self.age)
obj = People("egon",19)
print(obj) # print(obj.__str__())
class People:
def __init__(self,name,age):
self.name = name
self.age = age
self.f = open('a.txt',mode='rt',encoding='utf-8')
def __del__(self):
self.f.close()
obj = People("egon",19)
# del obj
print('=============end===================')

浙公网安备 33010602011771号