栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

python的标准库_python 抽象?

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python的标准库_python 抽象?


今天在看开源代码库的时候,遇到了抽象基类的使用,之前缺乏了解,因此在此记录一下抽象基类的适用场景以及在python中的使用。

抽象类

抽象类(abstract class)可以被视为其他类的模板。它可以创建一组必须在抽象类构建的任何子类中创建的方法。包含一个或多个抽象方法的类被称为抽象类。抽象方法是具有声明但没有实现的方法。使用抽象类的场景有两个:大型函数设计,为组件的不同实现提供公共接口。

抽象基类

有时候,写代码需要检查使用的对象是否遵守特定规范,即验证对象是否实现给定的方法或属性。可以使用Hasattr或Isinstance方法来检查输入是否符合特定的标识。但有时使用这些方法来检查数量巨大的不同性质和方法是不方便的。为此,Python引入了一个名为抽象基类(Abstract base Class,ABC)的概念(from abc import ABC)。它可以为子类定义一个公共应用程序接口(API)。ABC通过基类装饰方法作为抽象,然后将具体的类注册为抽象基类的实现。

抽象基类的主要目标是提供一种标准化方法来测试对象是否遵守给定规范。它还可以防止任何尝试实例化不覆盖超类中的特定方法的子类。在抽象类中,使用关键字@abstractmethod装饰,将方法变为抽象方法。

为什么要用抽象类?

比如有以下的代码:

class Lion:
    def give_food(self):
        print("Feeding a lion with raw meat!")

class Panda:
    def feed_animal(self):
        print("Feeding a panda with some tasty bamboo!")

class Snake:
    def feed_snake(self):
        print("Feeding a snake with mice!")

# Animals of our zoo:
leo = Lion()
po = Panda()
sam = Snake()

leo.give_food()
po.feed_animal()
sam.feed_snake()

本例中的代码是要实现给不同的动物喂养不同的食物的功能。可以发现,这种实现方式在动物数量增多时,会出现很多冗余的代码。现在的代码逻辑可以表示为:

为了减少代码冗余,更好维护代码,可以通过循环的方式实现流程化。假设想实现以下逻辑的代码:

# Put all the animals in a list:
zoo = [leo, po, sam] # Could be many more animals there!

# Loop through the animals and feed them
for animal in zoo:
	animal.feed() # This will throw an AttributeError!

但是直接循环会出现问题,因为每个动物的类中都是不同的喂养函数实现的。为了解决这个问题可以通过抽象基类实现。

from abc import ABC, abstractmethod
# abc is a builtin module, we have to import ABC and abstractmethod

class Animal(ABC): # Inherit from ABC(Abstract base class)
    @abstractmethod  # Decorator to define an abstract method
    def feed(self):
        pass

class Lion(Animal):
    def feed(self):
        print("Feeding a lion with raw meat!") 

class Panda(Animal): 
    def feed(self): 
        print("Feeding a panda with some tasty bamboo!") 

class Snake(Animal): 
    def feed(self): 
        print("Feeding a snake with mice!")

zoo = [Lion(), Panda(), Snake()]

for animal in zoo:
    animal.feed() # Now this won't throw an error!

这样就可以实现上述的代码逻辑,从而减少代码冗余,提高代码的可维护性。需要注意的是,在子类中继承抽象基类时,子类中重写的方法需要跟父类中的命名保持一致,否则会报错。以下是错误的写法:

class Panda(Animal): # If a class inherits from an ABC, it must implement all it's abstract methods!
    def wrong_name(self): # The method's name must match the name of the ABC's method
        print("Feeding a panda with some tasty bamboo!")
带有参数的抽象方法实现
from abc import ABC, abstractmethod

class Animal(ABC):
    @property                 
    def food_eaten(self):     
        return self._food

    @food_eaten.setter
    def food_eaten(self, food):
        if food in self.diet:
            self._food = food
        else:
            raise ValueError(f"You can't feed this animal with {food}.")

    @property
    @abstractmethod
    def diet(self):
        pass

    @abstractmethod 
    def feed(self, time):
        pass

class Lion(Animal):
    @property                 
    def diet(self):     
        return ["antelope", "cheetah", "buffaloe"]

    def feed(self, time):
        print(f"Feeding a lion with {self._food} meat! At {time}") 

class Snake(Animal):
    @property                 
    def diet(self):     
        return ["frog", "rabbit"]

    def feed(self, time): 
        print(f"Feeding a snake with {self._food} meat! At {time}") 


leo = Lion()
leo.food_eaten = "antelope" 
leo.feed("10:10 AM")
adam = Snake()
adam.food_eaten = "frog"
adam.feed("10:20 AM")
Feeding a lion with antelope meat! At 10:10 AM
Feeding a snake with frog meat! At 10:10 AM

如果运行一下代码则会报错:

leo = Lion()
leo.food_eaten = "carrot" 
leo.feed("10:10 AM")
You can't feed this animal with carrot.
抽象基类中的具体方法
# Python program invoking a
# method using super()

import abc
from abc import ABC, abstractmethod

class R(ABC):
	def rk(self):
		print("Abstract base Class")

class K(R):
	def rk(self):
		super().rk()
		print("subclass ")

# Driver code
r = K()
r.rk()
Abstract base Class
subclass
抽象属性

抽象类除了方法外还有属性,可以通过@abstractproperty定义具体类中的属性。

# Python program showing
# abstract properties

import abc
from abc import ABC, abstractmethod

class parent(ABC):
	@abc.abstractproperty
	def geeks(self):
		return "parent class"
		
class child(parent):
	@property
	def geeks(self):
		return "child class"

try:
	r =parent()
	print( r.geeks)
except Exception as err:
	print (err)

r = child()
print (r.geeks)
Can't instantiate abstract class parent with abstract methods geeks
child class

在上面的示例中,无法实例化基类,因为它只有属性获取方法的抽象版本。

抽象类实例化

抽象类是不完整的,因为它们的方法仅仅是形式上的,没有具体的定义(函数体为pass)。如果Python允许为抽象类创建对象,调用抽象方法时使用该对象,但没有实际实现调用。因此,将抽象类作为模板,并根据需要,将其扩展并在使用它之前构建它。抽象类不是一个具体的类,它无法实例化。当为抽象类创建一个对象时,它会引发错误。

# Python program showing
# abstract class cannot
# be an instantiation
from abc import ABC,abstractmethod

class Animal(ABC):
	@abstractmethod
	def move(self):
		pass
		
class Dog(Animal):
	def move(self):
		print("I can bark")

class Lion(Animal):
	def move(self):
		print("I can roar")

c=Animal()
TypeError                                 Traceback (most recent call last)
 in 
     21                 print("I can roar")
     22 
---> 23 c=Animal()

TypeError: Can't instantiate abstract class Animal with abstract methods move
metaclass=abc.ABCmeta

有些比较早的教程中会出现以下的实现方式,在最新的python实现中,使用上述的方式即可。

metaclass提供了一种称为寄存器方法的方法,可以通过其实例调用。通过使用该寄存器方法,任何抽象基类都可以成为任何任意具体的类的祖先。

import abc
  
  
class AbstractClass(metaclass=abc.ABCmeta):
    def abstractfunc(self):
        return None
  
  
print(AbstractClass.register(dict))

参考:

https://www.geeksforgeeks.org/abstract-classes-in-python/
https://www.geeksforgeeks.org/abstract-base-class-abc-in-python/
https://blog.teclado.com/python-abc-abstract-base-classes/

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/783094.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号