一 多态
1、什么是多态
多态指的是同一种事物有多种形态,比如动物有多种形态:猫、狗、猪
class Animal:
pass
class People(Animal):
pass
class Dog(Animal):
pass
class Pig(Animal):
pass
2、为何要有多态
多态具有多态性,多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象
多态性的好处在于增强了程序的灵活性和可扩展性,比如通过继承Animal类创建了一个新的类,实例化得到的对象obj,可以使用相同的方式使用obj.talk()
class Animal: # 统一所有子类的方法
def say(self):
print('动物基本的发声频率。。。',end=' ')
class People(Animal):
def say(self):
super().say()
print('嘤嘤嘤嘤嘤嘤嘤')
class Dog(Animal):
def say(self):
super().say()
print('汪汪汪')
class Pig(Animal):
def say(self):
super().say()
print('哼哼哼')
obj1=People()
obj2=Dog()
obj3=Pig()
obj1.say()
obj2.say()
obj3.say()
因为所有类都具有 say方法,不用管什么对象类型,都可调用say 方法。因此这里可以通过定义接口功能来调用say 方法,使代码更为简洁
obj1=People()
obj2=Dog()
obj3=Pig()
def animal_say(animal):
animal.say()
animal_say(obj1) #动物基本的发声频率。。。 嘤嘤嘤嘤嘤嘤嘤
animal_say(obj2) #动物基本的发声频率。。。 汪汪汪
animal_say(obj3) #动物基本的发声频率。。。 哼哼哼
案例:Python中一切皆对象,本身就支持多态性
#方式一:都有__len__方法
print('hello'.__len__()) # 5
print([1,2,3].__len__()) #3
print({'a':1,'b':2}.__len__()) #2
#方式二
#定义检测长度函数
def my_len(val):
return val.__len__()
#调用函数
print(my_len('hello')) #5
print(my_len([1,2,3])) #3
print(my_len({'a':1,'b':2})) #2
#方式三
len('hello') #5
len([1,2,3]) #3
len({'a':1,'b':2}) #2
3 限制子类必须拥有父类方法名
多态性的本质在于不同的类中定义有相同的方法名,这样我们就可以不考虑类而统一用一种方式去使用对象,可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
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 = Animal() #报错《Can't instantiate abstract class Animal with abstract methods talk》,抽象类不能被实例化
cat = Cat() ## 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
4 鸭子类型
依赖于继承,只需要制造出外观和行为相同对象,同样也可以实现不考虑对象类型而使用对象,这正是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
二 绑定方法与非绑定方法
绑定方法:特殊之处在于将调用者本身当做第一个参数传入
绑定方法分为:绑定给对象的方法和绑定给类的方法
1 对象的绑定方法
调用者是对象,自动传入的是对象本身
案例:
class Mysql:
def __init__(self,ip,port):
self.ip=ip
self.port=port
def func(self):
print('%s:%s' %(self.ip,self.port))
obj1=Mysql('1.1.1.1',3306)
print(obj1.__dict__) #{'ip': '1.1.1.1', 'port': 3306}
obj1.func()
#结果展示
'''
{'ip': '1.1.1.1', 'port': 3306}
1.1.1.1:3306
'''
2 类的绑定方法
调用者是类,自动传入的是类本身
一般默认是对象的绑定方法,如果要指定一个方法绑定给类,需要给该方加上装饰器 classmethod ,即将函数装饰成绑定给类的方法
案例:
'''
settings.py
IP = '127.0.1'
PORT = '3306'
'''
import settings
class Mysql:
def __init__(self,ip,port):
self.ip=ip
self.port=port
def func(self):
print('%s:%s' %(self.ip,self.port))
@classmethod # 将下面的函数装饰成绑定给类的方法
def from_conf(cls):
print(cls) #<class '__main__.Mysql'>
return cls(settings.IP, settings.PORT) #通过调用类的绑定方法,返回实例化得到的对象
obj2 = Mysql.from_conf()
print(obj2.__dict__) #{'ip': '127.0.0.1', 'port': 3306}
#结果展示
'''
<class '__main__.Mysql'>
{'ip': '127.0.0.1', 'port': 3306}
'''
3 非绑定方法
没有绑定给任何人:调用者可是类,也可以是对象, 没有自动传参的效果,需要在指定方法上加装饰器 staticmethod,即将函数装饰城一个静态方法
案例:
class Mysql:
def __init__(self,ip,port):
self.nid=self.create_id()
self.ip=ip
self.port=port
@staticmethod # 将下述函数装饰成一个静态方法
def create_id():
import uuid
return uuid.uuid4()
@classmethod #将下述函数装饰城一个类的绑定方法
def f1(cls):
pass
def f2(self): #对象的绑定方法
pass
obj1=Mysql('1.1.1.1',3306)
print(Mysql.create_id) #<function Mysql.create_id at 0x10d8eb1e0>
print(obj1.create_id) #<function Mysql.create_id at 0x10d8eb1e0>
print(Mysql.f1) #<bound method Mysql.f1 of <class '__main__.Mysql'>>
print(obj1.f2) #<bound method Mysql.f2 of <__main__.Mysql object at 0x10f7c0400>>