【问题标题】:Multiple Inheritance Dependency - Base requires AbstractBaseClass多重继承依赖 - Base 需要 AbstractBaseClass
【发布时间】:2018-08-09 00:46:48
【问题描述】:

问题的要点:如果继承多个类,我如何保证如果一个类被继承,子对象也使用一个恭维抽象基类(abc)。

我一直在研究 python 的继承,想看看我能做什么很酷的事情,我想出了这个模式,这很有趣。

我一直在尝试使用它来更轻松地实现和测试与我的缓存交互的对象。我有三个模块:

  • ICachable.py
  • Cacheable.py
  • SomeClass.py

ICacheable.py

import abc

class ICacheable(abc.ABC):
    @property 
    @abc.abstractmethod
    def CacheItemIns(self):
        return self.__CacheItemIns
    @CacheItemIns.setter
    @abc.abstractmethod
    def CacheItemIns(self, value):
        self.__CacheItemIns = value
        return
    
    @abc.abstractmethod
    def Load(self):
        """docstring"""
        return

    @abc.abstractmethod
    def _deserializeCacheItem(self): 
        """docstring"""
        return

    @abc.abstractmethod
    def _deserializeNonCacheItem(self): 
        """docstring"""
        return

Cacheable.py

class Cacheable:
    
    def _getFromCache(self, itemName, cacheType,
                          cachePath=None):
            """docstring"""
    
            kwargs = {"itemName" : itemName, 
                      "cacheType" : cacheType,
                      "cachePath" : cachePath}
    
            lstSearchResult = CacheManager.SearchCache(**kwargs)
            if lstSearchResult[0]:
                self.CacheItemIns = lstSearchResult[1]
                self._deserializeCacheItem()
            else:
                cacheItem = CacheManager.NewItem(**kwargs)
                self.CacheItemIns = cacheItem
                self._deserializeNonCacheItem()
    
            return

SomeClass.py

import ICacheable
import Cacheable

class SomeClass(Cacheable, ICacheable):
    __valueFromCache1:str = ""
    __valueFromCache2:str = ""
    __CacheItemIns:dict = {}
    @property 
    def CacheItemIns(self):
        return self.__CacheItemIns
    @CacheItemIns.setter
    def CacheItemIns(self, value):
        self.__CacheItemIns = value
        return

    def __init__(self, itemName, cacheType):
        #Call Method from Cacheable
        self.__valueFromCache1
        self.__valueFromCache2
        self.__getItemFromCache(itemName, cacheType)
        return

    def _deserializeCacheItem(self): 
        """docstring"""
        self.__valueFromCache1 = self.CacheItemIns["val1"]
        self.__valueFromCache2 = self.CacheItemIns["val2"]
        return

    def _deserializeNonCacheItem(self): 
        """docstring"""
        self.__valueFromCache1 = #some external function
        self.__valueFromCache2 = #some external function
        return

所以这个例子有效,但可怕的是没有保证继承Cacheable 的类也继承ICacheable。这似乎是一个设计缺陷,因为Cacheable 本身毫无用处。然而,从我的子类/子类中抽象事物的能力是强大的。有没有办法保证Cacheable对ICacheable的依赖?

【问题讨论】:

  • 使Cacheable 成为ICacheable 的子类。
  • @mypetlion 让我们进入这个时代,“我怎么知道哪些方法需要在 Cacheable 中被覆盖”。代替 Cacheable 提供辅助实现的助手,它现在实际上是在实现 ICacheable。突然又回到了与 C# 和 Java 相同的问题,但没有它们拥有的一半继承工具(比如声明类范围的能力)。至少我是这么想的。
  • 为什么要指定私有方法,例如_deserializeNonCacheItem,作为接口的一部分?这些不应该是可见的,因此您实际上是在执行一个实现细节。另请注意,__CacheItemIns 是受 name mangling 约束的类私有成员 - ICacheable 除非声明它,否则永远不能使用它。见docs.python.org/3/tutorial/classes.html#private-variables

标签: python multiple-inheritance abstract-base-class


【解决方案1】:

如果您明确不想继承,可以将类注册为 ABC 的虚拟子类。

@ICacheable.register
class Cacheable:
    ...

这意味着Cacheable 的每个子类也被自动视为ICacheable 的子类。如果您有一个有效的实现,而该实现会因遍历非功能性抽象基类而减慢速度,这将非常有用,例如用于super 电话。

但是,ABC 不仅仅是接口,可以从它们继承。事实上,ABC 的部分好处是它强制子类实现所有抽象方法。一个中间帮助类,例如Cacheable,在它从未实例化时不实现所有方法是可以的。但是,任何实例化的非虚拟子类都必须是具体的。

>>> class FailClass(Cacheable, ICacheable):
...    ...
...
>>> FailClass()
TypeError: Can't instantiate abstract class FailClass with abstract methods CacheItemIns, Load, _deserializeCacheItem, _deserializeNonCacheItem

请注意,如果您

  • 总是子类为class AnyClass(Cacheable, ICacheable):
  • 从不实例化Cacheable

这在功能上等同于从ICacheable 继承的Cacheable。方法解析顺序(即继承菱形)是相同的。

>>> AnyClass.__mro__
(__main__. AnyClass, __main__.Cacheable, __main__.ICacheable, abc.ABC, object)

【讨论】:

  • 使用虚拟子类是否意味着 Cacheable 必须隐含 ICacheable?这里的部分想法是 Cacheable 没有实现 ICacheable。
  • Cacheable 只需要完全实现ICacheable 如果Cacheable 是一个具体 子类并被实例化。虚拟子类不继承任何东西,包括 ABC 一致性检查。一个具体但不完整的子类只是一个更专业但仍然抽象的基类。
  • Cacheable 不是总是被实例化为构造函数链的一部分吗?
  • 这不是 Python 中类型的工作方式。如果您实例化 SomeClass,则会实例化并初始化 SomeClass 类型的对象 - 它的 MRO 仅包含 Cacheable。现在,该实例可以选择像Cacheable 实例一样初始化自身(通过Cacheable.__init__super().__init__),但在所有这些过程中它仍然是SomeClass 类型。这意味着它具有满足ICacheable 所需的所有具体方法。
  • 有趣,我今天要玩这个。如果我让它工作,我会回来并标记这篇文章是正确的。谢谢。
猜你喜欢
  • 2018-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-12
  • 2021-03-29
  • 2015-06-23
  • 2013-04-01
  • 2023-03-28
相关资源
最近更新 更多