joomla 2.5:你的网站建设_使用与管理 下载,电子工程师资格证,宁德网站建设,有赞微商城官网登录目录 协议和鸭子类型抽象基类定义抽象基类使用抽象基类直接继承通过注册#xff08;register#xff09; __subclasshook__魔法方法 协议和鸭子类型
python中有大量的魔法方法#xff0c;python所谓基于协议编程#xff0c;就是依赖这些魔法方法。
什么意思呢#xff1f… 目录 协议和鸭子类型抽象基类定义抽象基类使用抽象基类直接继承通过注册register __subclasshook__魔法方法 协议和鸭子类型
python中有大量的魔法方法python所谓基于协议编程就是依赖这些魔法方法。
什么意思呢
比如我们想要实现一个对象让他可以有序列的特征可切片、可遍历等特征那么我们只要实现__len__和 __getitem__ 两个方法即可。任何类 要使用标准的签名和语义实现了这两个方法就能用在任何期待序列的地方。是不是哪个类的子类无关紧要只要提供了所需的方法即可。
如下例
class FrenchDeck:def __init__(self, seq):self._cards list(seq)def __len__(self):return len(self._cards)def __getitem__(self, position):return self._cards[position]if __name__ __main__:obj FrenchDeck([1, 2, 3, 4, 5])# 可切片print(obj[2])# 可遍历for i in obj:print(i)FrenchDeck对象没有任何人告诉过它是一个序列但是由于python基于协议来表现它的表现却很像一个序列那么就可以说FrenchDeck对象时一个序列。这就是鸭子类型。
可以通俗理解为不管这个类是不是一个鸭子只要它的表现像鸭子有羽毛、会游泳嘎嘎叫那么久可以称之为鸭子。
抽象基类
前面也了解到python是基于协议的引入抽象基类之前Python 就已经非常成功了即便现在也很少有代码使用抽象基类。
一般也不建议我们自己去定义抽象基类抽象基类一般只会在框架中看到这里只是为了学习知识。
抽象类有什么作用抽象基类是一种特殊的类它只用于作为其他类的基础父类。它定义了一些必须要实现的方法而这些方法在子类中必须被重写以便于子类能够正常工作。它提供了一种方式来定义和强制执行类必须实现的方法一般自己不能被实例化。
定义抽象基类
其实python中并没有像java中有interface关键字可以来定义抽象基类。python中定义抽象基类一般是通过abc模块创建它提供了ABCAbstract Base Class类来实现这个功能。要定义一个抽象基类请从ABC类派生一个类并使用abstractmethod装饰器将方法标记为抽象方法。
示例如下
from abc import ABC, abstractmethodclass MyAbstractClass(ABC):abstractmethoddef my_abstract_method(self):pass在上面的代码中MyAbstractClass继承自ABC类并且使用了abstractmethod装饰器来声明一个抽象方法my_abstract_method()。注意到在该函数内部并没有实现任何具体的逻辑而是使用了pass语句进行占位符处理。这表明该方法必须在子类中被重写。
当我们定义完抽象基类之后我们可以将其作为其他类的基类使得子类必须实现抽象基类中定义的抽象方法并且在运行时检查子类是否实现了这些方法。如果子类没有实现完整的抽象方法那么在实例化时就会引发TypeError异常。
需要注意的是抽象类可能同时包含具体方法和抽象方法。
使用抽象基类
具体化抽象类可以有两种方式一种通过注册register另外一种通过继承。
直接继承
当一个类继承了抽象基类那么子类就必须实现抽象基类里的抽象方法否则报错。
from abc import ABC, abstractmethodclass Shape(ABC):abstractmethoddef area(self):passabstractmethoddef perimeter(self):passclass Square(Shape):def __init__(self, side):self.side sidedef area(self):return self.side ** 2def perimeter(self):return self.side * 4s Square(5)
print(s.area()) # 输出 25
print(s.perimeter()) # 输出 20在上面的代码中定义了一个抽象基类Shape并在其中定义了两个抽象方法area和perimeter。然后我们定义了一个Square类继承自Shape类并实现了其中的两个抽象方法。
当我们创建Square类的对象时它将拥有Shape类中定义的接口和行为。通过这种方式我们可以强制要求子类定义特定的方法并在需要时使用它们。
通过注册register
Python的abc模块提供了一个register方法可以用于将类注册为抽象基类的虚拟子类即使它没有明确继承该类也可以被视为其子类。
下面是一个示例代码
from abc import ABC, abstractmethodclass Shape(ABC):abstractmethoddef area(self):passabstractmethoddef perimeter(self):passclass Square:def __init__(self, side):self.side sidedef area(self):return self.side ** 2def perimeter(self):return self.side * 4Shape.register(Square)s Square(5)
print(isinstance(s, Shape)) # 输出 True
print(s.area()) # 输出 25
print(s.perimeter()) # 输出 20在这个示例中我们定义了一个Shape类作为抽象基类并定义了两个抽象方法area和perimeter。然后我们定义了一个Square类它没有明确继承自Shape类但我们通过调用Shape.register方法将其注册为Shape类的虚拟子类。因此即使Square类没有显式地继承Shape类我们仍然可以将其视为Shape类的实例并在其中调用Shape类中定义的方法。
在python3.3后也可以通过装饰器的方式
Shape.register
class Square:...通过使用register方法我们可以更灵活地定义抽象基类和子类之间的关系并确保所有必需的方法都得到了正确实现。
__subclasshook__魔法方法
在 Python 中__subclasshook__方法是一个特殊方法它可以用来控制子类的继承关系。当一个类被调用了 issubclass() 函数时Python 会自动搜索其基类中是否定义了 subclasshook 方法。如果找到了就会用该方法返回的布尔值来判断该类是否为子类。
几点说明:
该方法定义在抽象基类中该方法必须定义为类方法该方法有三个返回值 True: 如果测试类被认为是子类 False: 如果测试类不被认为是子类 NotImplemented: 子类未实现执行方法
先看下python里自己实现的一个类Sized其源码如下
class Sized(metaclassABCMeta):__slots__ ()abstractmethoddef __len__(self):return 0classmethoddef __subclasshook__(cls, C):if cls is Sized:return _check_methods(C, __len__)return NotImplemented如上所示我们看到了它实现了__subclasshook__方法检查子类和子类的mro上所有的类是否有__len__方法如果没有返回NotImplemented。当然我们不必继承Sized而是使用虚拟子类virtual subclass技术只实现__len__协议就可以隐式继承了Sized。
下面我们仿造Sized自定义一个抽象基类
import abc
class Base(abc.ABC):abc.abstractmethoddef my_protocol(self):自定义协议classmethoddef __subclasshook__(cls, subclass):if cls is Base:if any(my_protocol in B.__dict__ for B in subclass.__mro__):return Truereturn NotImplemented接下来我们定义一个子类隐式继承Base
class MyClass:def my_protocol(self):passif __name__ __main__:obj MyClass()# isinstance查询示例obj是否是Base的实例print(isinstance(obj, Base)) # True# issubclass查询MyClass是否是Base的子类print(issubclass(MyClass, Base)) # True# 查询Base类的虚拟子类print(Base._abc_impl)如上所示我们只需要实现my_protocol协议就会隐式继承自抽象基类这样就实现了虚拟子类的创建。
参考
https://www.python51.com/jc/17075.html