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
ますcom
。hasattr(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
集積短いシームレス。したがって、抽象基本クラスを設計し、サブクラスが特定のメソッドを実装する必要があることを指定する必要があります。システムを記述する際の枠組みを書くときに、例えば、我々はフレームワークを使用する人々は、かどうかはわかりません置き換えるとそれを
redis
やcache
待ち時間、私たちはこれらを書いた後、ユーザーが必要としないことを願っていますかコールする必要性を減らします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つabc
はcollections
内部にあり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__
この魔法の機能は非常に重要です。
質問を思い出してください。最初に示した例com
はCompany
、Company
クラスによってインスタンス化されたオブジェクトであり、クラスは抽象基本クラスSizedを継承していません。それでは、なぜisinstance(com,Sized)
それcom
がSized
型であると判断できるのでしょうか。
答えはSized
、__subclasshook__
この魔法の関数が抽象基本クラスで定義されているということです。ソースコードは次のとおりです(完全なものは上に示され、一部はここに簡単に示されています)
@classmethod
def __subclasshook__(cls,C):
if cls is Sized:
return _check_methods(C,'__len__')
return NotImplemented
__subclasshook__
関数は最初に_check_methods
この関数を呼び出して、渡されたオブジェクトCに__len__
メソッドがあるかどうかを確認し、ある場合はを返しますTrue
。Sized
基本クラスのこの魔法の関数も、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多重継承)を使用することをお勧めします。抽象ベースを使用することはお勧めしません。クラス(それで私がこれを学ぶ理由==)、抽象基本クラスは設計で使いすぎやすく、理解するのが難しいためです。