Pythonクラスとオブジェクトの研究ノート-抽象的な基本クラス

4.2_Abstract基本クラス(abcモジュール)

リファレンスブログ

1.まえがき(ナンセンス、読まないでください)

抽象基本クラスは中国のjavaインターフェースのようなものでjava多重継承を実装できません。つまり、1つのクラスしか継承できませんが、複数のインターフェースを継承でき、インターフェースをインスタンス化に使用することはできません。

python抽象基底クラスは、いずれかのインスタンス化することはできません。pythonこれは動的言語であり、変数を定義するときに変数タイプを宣言する必要はありません。変数は単なるシンボルであり、任意のタイプのオブジェクトを指すことができます。

の任意のタイプのデータをpython任意の変数にコピーして変更できます。多型をサポートする言語であるjavaため、そのような多型実装する必要はありませんpython

静的言語と比較して、動的言語は変数タイプを指定する必要がありません。これにより、python間違ったコードを記述した場合、実行したときにのみエラーが発生します。つまり、タイプチェックを実行できません。

デザインPythonクラスを使用する場合、最初にダックタイプを配置する必要があります。クラスが持つ特性またはタイプは、クラスが内部で実装する魔法の関数によって異なります。魔法の関数が異なれば、クラスに異なる特性が与えられます。

アヒルのタイプと魔法の関数は、Python言語全体の基礎を形成します。これは、Pythonのプロトコルとも言えます。Python自体は、特定のクラスや特定のインターフェイスを継承することによって特定の機能を実装するのではなく、魔法の関数のクラスを指定して特定のタイプのオブジェクトにすることによって実装すると言えます。たとえば、文字列__repr__表現:__str__;反復関連:__iter____next__;呼び出し可能:__call__など。プログラミングするときは、このプロトコルに従ってください。

2つの抽象的な基本クラス

  • 基本クラスにいくつかのメソッドを設定します。この基本クラスを継承するすべてのクラスは、この抽象基本クラスのメソッドをオーバーライドする必要があります。
  • 抽象基本クラスはインスタンス化には使用できません。
  • abcモジュールの役割:
    Python自体は抽象クラスとインターフェースメカニズムを提供しません。抽象クラスを実装する場合は、abcモジュールを使用できます。abcは、Abstract BaseClassの略語です。

質問

  • Pythonはダックタイプに基づいて設計されているのに、なぜ抽象基本クラスの概念があるのですか?特定のメソッドを直接実装するだけでは不十分ですか?なぜ継承する必要があるのですか?

回答

抽象基本クラスの2つのアプリケーションシナリオ

  • 最初のアプリケーションは、特定のクラスに特定のメソッドがあるかどうかを確認することです(オブジェクトのタイプを判別します)
class Company(object):
    def __init__(self,employee_list):
        self.employee = employee_list
    def __len__(self):
        return len(self.employee)
   
com=Company(['ming','ming2'])
print(hasattr(com,'__len__')) # True 
# hasattr内置函数 某一个对象是否具有某种属性,类中函数其实就是一个属性
print(len(com)) # 2 

したがって、それを学んだ人はクラスがインスタンス化するオブジェクトのタイプjavaを判断する傾向があることを知っておく必要がありCompanyますcomhasattr(com,'__len__')このメソッドを呼び出してオブジェクト判断する代わりに

上記の内容に基づいて、抽象基本クラスの最初のアプリケーションシナリオを使用して、特定のタイプのオブジェクトのタイプを判別します。抽象基本クラスがない場合は、hasattr()このメソッドを使用する必要があります抽象基本クラスの使用は次のようになります。

from collections.abc import Sized
isinstance(com,Sized) # True
# isinstance 判断某一对象是否是指定的类型

つまり、Sizedを継承して__len__メソッドを作成、この時点でinstance(对象, Sized)True返すことができます。

最後に、上記でインポートされたSized抽象基本クラスのソースコードを見てください。

#所有的抽象基类中的metaclass都必须是ABCMeta
class Sized(metaclass = ABCMeta):
    __slots__ = ()
    @abstractmethod
    def __len__(self):
        return 0
    
    @classmethod
    def __subclasshook__(cls,C):
        if cls is Sized:
            return _check_methods(C,'__len__')
        return NotImplemented
  • 2番目のアプリケーションは、特定のサブクラスに特定のメソッドを実装するように強制することです。

    このような内部実装webフレームワーク(例えばDjango)、フレームは望ましいかもしれないcacheに統合キャッシュ、および将来的に望ましいかもしれないredisか、cacheまたはmemorychache置換、または使用することができるredisか、cacheまたはmemorycache既存で置換する、コンポーネントをカスタマイズcache集積短いシームレス。

    したがって、抽象基本クラスを設計し、サブクラスが特定のメソッドを実装する必要があることを指定する必要があります。システムを記述する際の枠組みを書くときに、例えば、我々はフレームワークを使用する人々は、かどうかはわかりません置き換えるとそれをrediscache待ち時間、私たちはこれらを書いた後、ユーザーが必要としないことを願っていますかコールする必要性を減らしますredisか、cacheコードなので、抽象基本クラス(主キー)は事前に合意されます

class CacheBase():
    def get(self,key):
        pass
    def set(self,key,value):
        pass

CacheBase抽象基本クラスは、getメソッド(キャッシュからデータを取得して取得key)とsetメソッド(追加value)を定義します。この抽象基本クラスを継承する場合、ユーザーはこれら2つのメソッドを再度実装する必要があります(呼び出しを実装しない場合は、エラーなので、ユーザーにこれらのメソッドの実装を強制します)。

これらのインターフェースが事前に定義されていない場合、ユーザーはクラスに戻って後で使用するコードを書き直します。事前に契約が結ばれている場合、ユーザーは抽象クラスのサブクラスを実装し、それを構成するだけで済みます。構成ファイル。主キーは次のとおりです。合意どおりに実装された場合、交換すると非常に便利です。

多くの場合、フレームワークを作成するときに考慮する必要があります。これは、システムのスケーラビリティに非常に役立ちます。

3.抽象基本クラスをシミュレートする方法は?

コードは以下のように表示されます

#假如有一个抽象基类CacheBase,用户在继承它时必须实现get,set方法
class CacheBase():
    def get(self,key):
        raise NotImplementedError
    def set(self,key,value):
        raise NotImplementedError

class RedisCache(CacheBase):
    pass

redise_cache = RedisCache()
redis_cache.set('key','value') # NotImplementedError
# 调用set方法会报错

サブクラスがsetメソッドを再実装する場合、例外をスローせずにサブクラスメソッドが呼び出されます。

class CacheBase():
    def get(self,key):
        raise NotImplementedError
    def set(self,key,value):
        raise NotImplementedError

class RedisCache(CacheBase):
    def set(self,key,value):
        pass

redise_cache = RedisCache()
redis_cache.set('key','value') # 无异常

上記のシミュレーションの欠点は、setメソッドが呼び出されたときにこの例外がスローされることです。オブジェクトの初期化中に例外をスローする場合はどうなりますか?

abcモジュールを使用する必要があります。モジュールには2つあり、1つは全体的な状況下にあり、もう1つabccollections内部にありabcます。

  • abcモジュールを使用して抽象基本クラス実装する
import abc
class CacheBase():
    
    @abc.abstractmethod #装饰器 抽象方法(设计方法)
    def get(self,key):
        pass
    
    @abc.abstractmethod #装饰器 抽象方法(设计方法)
    def set(self,key,value):
        pass
        
class RedisCache(CacheBase): #没有重写方法
        pass

redise_cache = RedisCache()
# redis_cache.set('key','value') # 无异常

サブクラスがRedisCacheメソッドをオーバーロードないため、上記のコードは初期化中に例外をスローしますTypeError: Can't instantiate abstract class RedisCache with abstract methods get,set

class RedisCache(CacheBase): 
    def get(self,key):
        pass
    def set(self,key,value):
        pass

redise_cache = RedisCache() #RedisCache子类实现get和set方法后,运行不抛异常了
  • データ構造のインターフェースを理解できるように、いくつかの一般的な抽象基本クラスがPythonで実装されています。ソース_collections_abc.pyファイルの先頭に、多くの定義済みの抽象基本クラスがあります。
from abc import ABCMeta, abstractmethod
import sys

__all__ = ["Awaitable", "Coroutine",
           "AsyncIterable", "AsyncIterator", "AsyncGenerator",
           "Hashable", "Iterable", "Iterator", "Generator", "Reversible",
           "Sized", "Container", "Callable", "Collection",
           "Set", "MutableSet",
           "Mapping", "MutableMapping",
           "MappingView", "KeysView", "ItemsView", "ValuesView",
           "Sequence", "MutableSequence",
           "ByteString",
           ]

最初のもののように、1つを見てくださいAwaitable

class Awaitable(metaclass=ABCMeta):

    __slots__ = ()

    @abstractmethod
    def __await__(self):
        yield

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Awaitable:
            return _check_methods(C, "__await__")
        return NotImplemented

それらの中で、__subclasshook__この魔法の機能は非常に重要です。

質問を思い出してください。最初に示した例comCompanyCompanyクラスによってインスタンス化されたオブジェクトであり、クラスは抽象基本クラスSizedを継承していません。それでは、なぜisinstance(com,Sized)それcomSizedであると判断できるのでしょうか。

答えはSized__subclasshook__この魔法の関数が抽象基本クラスで定義されているということです。ソースコードは次のとおりです(完全なものは上に示され、一部はここに簡単に示されています)

@classmethod
def __subclasshook__(cls,C):
    if cls is Sized:
        return _check_methods(C,'__len__')
    return NotImplemented

__subclasshook__関数は最初に_check_methodsこの関数を呼び出して、渡されたオブジェクトCに__len__メソッドがあるかどうかを確認し、ある場合はを返しますTrueSized基本クラスのこの魔法の関数isinstance(com,Sized)を返すことができるよう抽象化されていますTrue

もちろん、isinstanceとしてpython、内蔵機能、機能は間違いなく、より単純なよりです。Sizedクラス内の__subclasshook__関数呼び出すだけでなく他の多くの試みも行います。たとえば、継承チェーンを見つけるには、次のようにします。

class A:
    pass
class B(A):
    pass
b = B()
print(isinstance(b,A))  # True

概要:pythonダックタイプを使用するときはできるだけ使用するようにしてください。非常に柔軟性があります。特定のインターフェイスを継承する必要がある場合は、Mixin(Python多重継承)を使用することをお勧めします。抽象ベースを使用することはお勧めしません。クラス(それで私がこれを学ぶ理由==)、抽象基本クラスは設計で使いすぎやすく、理解するのが難しいためです。

おすすめ

転載: blog.csdn.net/weixin_43901214/article/details/106864388