面向对象之多态
一、接口思想
1、接口:建立关联的桥梁,方便管理代码
python中没有接口语法
def jiao(): pass def chi(): pass def pao(): pass # 清晰知道操作的功能,但不明确操作的具体对象 print(len('123')) # 清晰最大操作的对象,但不明确具体的操作方法 print('123'.__len__())
2、接口类:
用来定义功能的类,为继承它的子类提供功能的,该类的功能方法一般不需要有实现体,实现体有继承它的子类自己去实现
class PetInterface: def close_master(self): pass class WatchInterface: def watch_door(self): pass class Dog(PetInterface, WatchInterface): def jiao(self): pass def chi(self): pass def pao(self): pass # 一定要重写接口的方法 pass class Cat(PetInterface, WatchInterface): pass
二、抽象类思想
抽象父类:拥有抽象方法(子类共有的方法,但是父类不能有具体的实现体)的父类
抽象方法:方法名是具体的,但是实现体是抽象的(在子类中重写来具象化)
# python中借助abc来实现抽象父类 import abc # abstract base class class Quan(metaclass=abc.ABCMeta): def __init__(self, name): self.name = name def run(self): print(self.name + 'running') # 抽象父类中的抽象方法,在继承它的子类中必须有自己的实现体 # -- 抽象父类中的抽象方法实现体就没有意义,实现与不实现都是pass填充 @abc.abstractmethod def chi(self): # print(self.name + '肉') pass @abc.abstractmethod def jiao(self): # print('汪汪汪') pass @classmethod @abc.abstractmethod def fn(cls): pass class Dog(Quan): @classmethod def fn(cls): pass def kanmen(self): print(self.name + '看门') def chi(self): super().chi() print(self.name + '吃狗粮') def jiao(self): print('汪汪汪') class Wolf(Quan): @classmethod def fn(cls): pass def bulie(self): print(self.name + '捕猎') def chi(self): print(self.name + '吃肉') def jiao(self): print('嗷嗷嗷') dog = Dog('来福') wolf = Wolf('常委') dog.jiao() # 汪汪汪 wolf.jiao() # 嗷嗷嗷 dog.run() # 来福running wolf.run() # 常委running dog.chi() # 来福吃狗粮 wolf.chi() # 常委吃肉
三、多态
1、定义:对象的多种状态 - 父类对象的多种(子类对象)状态
2、多态的体现:
功能或是需求,需要父类的对象,可以传入父类对象或任意子类对象均可以
注:一般都是规定需要父类对象,传入子类对象
import abc class People(metaclass=abc.ABCMeta): def __init__(self, name): self.name = name @abc.abstractmethod def speak(self): pass class Chinese(People): def speak(self): print('说中国话') class England(People): def speak(self): print('说英国话') if __name__ == '__main__': # 多态的体现:功能或是需求,需要父类的对象,可以传入父类对象或任意子类对象均可以 # 注:一般都是规定需要父类对象,传入子类对象 def ask_someone(obj): print('让%s上台演讲' % obj.name) # 父类提供,自己直接继承 obj.speak() # 父类提供,只不过子类重写了 ch = Chinese('王大锤') en = England('Tom') ask_someone(ch) # 让王大锤上台演讲 # 说中国话 ask_someone(en) # 让Tom上台演讲 # 说英国话
四、鸭子类型
1、定义:
①先规定:有什么属性及什么方法的类的类型叫鸭子类型
②这些类实例化出的对象,都称之为鸭子,都可以作为需求对象的一种具体体现
class Test: def __init__(self, name): self.name = name def speak(self): print('说鸟语') if __name__ == '__main__': def ask_someone(obj): print('让%s上台演讲' % obj.name) # 需要一个对象,这个对象需要什么特点,有name,有speak, obj.speak() # 那就制定一个规则,只要有一个对象,它有name属性,speak方法,都叫鸭子 # 规定的规则能随意改变,需要一个什么样的对象,有这样特征的都叫鸭子 test = Test('鸭子') # 规定一个对象有什么属性和方法,能提供该属性和方法的对象都叫鸭子 ask_someone(test) # 让鸭子上台演讲 # 说鸟语
2、例子
# 例: # 需求:需要一个对象,该对象有name属性及speak方法,就可以作为一种状态的体现被传入 def ask_someone(obj): print('让%s上台演讲' % obj.name) obj.speak() # 1.先规定:有什么属性及什么方法的类的类型叫鸭子类型 # 2.这些类实例化出的对象,都称之为鸭子,都可以作为需求对象的一种具体体现 class A: # 能有自己特有的属性和方法,可以和B完全不一样,但是必须有鸭子类型规定的属性和方法,不然就不是鸭子类型 def __init__(self, name): self.name = name def speak(self): print('说AAAA') class B: # 能有自己特有的属性和方法,可以和A完全不一样,但是必须有鸭子类型规定的属性和方法,不然就不是鸭子类型 def __init__(self, name): self.name = name def speak(self): print('说BBBB') ask_someone(B('B')) # 让B上台演讲 # 说BBBB
五、格式化和析构方法
1、应用:
class A: def __init__(self, name, age): self.name = name self.age = age # ①格式化方法:在外界打印该类对象是被调用 # 格式化外界直接打印该类对象的字符串表示结果 def __str__(self): return '<name:%s | age:%s>' % (self.name, self.age) # ②析构方法:在对象被消耗的那一刹那被调用,在被消耗前可以做一些事情 def __del__(self): # del会在self代表的对象被消耗的时候被调用 # 我们可以在析构函数中释放该对象持有的其他资源, # 或者将一些持有资源持久化(保存到文件或数据库中) del self.name # 也可以将name存起来 a = A('老王', 88) print(a, type(a)) # <name:老王 | age:88> <class '__main__.A'> import time time.sleep(3) print('文件马上执行完毕,a就会被销毁')
2、了解:
class B: # 了解:对象.语法的内部实现 def __setattr__(self, key, value): # print(key, value) b.__dict__[key] = value # b.__dict__[key] = value.lower() # b.__dict__['xyz'] = 'XYZ' # 了了解:将对象添加属性的方式可以同字典形式 def __setitem__(self, key, value): self.__dict__[key] = value b = B() b.name = 'BBB' # == b.__dict__['name'] = 'BBB' print(b.name) # BBB b['age'] = 18 print(b.age) # 18 # 访问还是通过.语法访问
六、反射(*****)
1、定义:通过字符串与类和对象的属性(方法)建立关联
2、应用:
class People: country = "China" def __init__(self, name): self.name = name def tell(self): print('%s is aaa' % self.name) obj = People('egon') # 1、hasattr(判断'country'是否在People的名称空间里) print(hasattr(People, 'country')) # True # 'country'是否在People的名称空间里 print('country' in People.__dict__) # True # 不同的两种方法 print(hasattr(obj, 'name')) # True print(hasattr(obj, 'country')) # True print(hasattr(obj, 'tell')) # True # 2、getattr(查找) x = getattr(People, 'country') # China print(x) x = getattr(People, 'country1',None) print(x) # None f = getattr(obj, 'tell', None) print(f == obj.tell) # True f() # egon is aaa obj.tell() # egon is aaa # 3、setattr(增值) People.x = 111 setattr(People, 'x', 111) print(People.x) # 111 obj.age = 18 setattr(obj, "age", 18) print(obj.__dict__) # {'name': 'egon', 'age': 18} # 4、delattr(删值) delattr(People, "country") # ==del People.country print(People.__dict__) # 从People的名称空间中删除了'country' delattr(obj, "name") # ==del obj.name print(obj.__dict__) # {} # 删除obj里的’name’后,obj中的名称空间为空
补充:
①__getattr__:在对象获取它没有的属性和方法的时候自动触发
②__setattr__:在对象点属性设置属性值的时候自动触发
class Demo(object): def __init__(self,name): self.name = name def __getattr__(self, item): print('这个对象想获取一个它没有的属性>>>:',item) return 123 def __setattr__(self, key, value): print(key, value) obj = Demo('jason') obj.password = '123' print(obj.name) print(obj.yyy) # 结果为 # name jason # password 123 # 这个对象想获取一个它没有的属性>>>: name # 123 # 这个对象想获取一个它没有的属性>>>: yyy # 123
3、小练习:
class Foo: def run(self): while True: cmd = input('cmd>>: ').strip()if hasattr(self, cmd): func = getattr(self, cmd) func() def download(self): print('download....') def upload(self): print('upload...') obj=Foo() obj.run() # 结果为 cmd>>: download download.... cmd>>: upload upload...
4、总结
# 总结: # 类的属性用类来操作 # 对象的属性用对象来操作 # 方法建议使用类来操作,得到的方法调用时 # -- 对象的方法要传入具体的对象 # -- 类的方法不需要传入参数 class C: def fn(self): print('fn') @classmethod def func(cls): print('func') fn = getattr(C, 'fn') c = C() fn(c) # fn func = getattr(C, 'func') func() # func