オブジェクト指向の基礎
最初に、最も基本的なPythonオブジェクト指向アプリケーションコードの一部を見てみましょう。その長さを恐れないでください。
class Document():
def __init__(self, title, author, context):
print('init function called')
self.title = title
self.author = author
self.__context = context # __开头的属性是私有属性
def get_context_length(self):
return len(self.__context)
def intercept_context(self, length):
self.__context = self.__context[:length]
harry_potter_book = Document('Harry Potter', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
print(harry_potter_book.title)
print(harry_potter_book.author)
print(harry_potter_book.get_context_length())
harry_potter_book.intercept_context(10)
print(harry_potter_book.get_context_length())
print(harry_potter_book.__context)
########## 输出 ##########
init function called
Harry Potter
J. K. Rowling
77
10
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-b4d048d75003> in <module>()
22 print(harry_potter_book.get_context_length())
23
---> 24 print(harry_potter_book.__context)
AttributeError: 'Document' object has no attribute '__context'
このコードを参照して、最初にいくつかの概念を簡単に説明します。
- クラス:類似したもののグループ。ここではPythonクラスに対応します。
- オブジェクト:コレクション内のオブジェクト。コード内のharry_potter_bookなど、クラスによって生成されたオブジェクトに対応します。
- 属性:上記のコードのタイトル、作成者、__コンテキストなどのオブジェクトの静的機能。
- 関数:上記のコードのintercept_context()関数など、オブジェクトの特定の動的機能。
もちろん、そのようなステートメントは厳密でも十分でもありませんが、オブジェクト指向プログラミングをまったく理解していない場合は、直感的にすぐに理解できます。
クラス、同じプロパティと機能を持つオブジェクトのコレクション。
次に、コードを今すぐ解釈しましょう。
class Document():
def __init__(self, title, author, context):
print('init function called')
self.title = title
self.author = author
self.__context = context # __开头的属性是私有属性
def get_context_length(self):
return len(self.__context)
def intercept_context(self, length):
self.__context = self.__context[:length]
harry_potter_book = Document('Harry Potter', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
print(harry_potter_book.title)
print(harry_potter_book.author)
print(harry_potter_book.get_context_length())
harry_potter_book.intercept_context(10)
print(harry_potter_book.get_context_length())
print(harry_potter_book.__context)
########## 输出 ##########
init function called
Harry Potter
J. K. Rowling
77
10
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-b4d048d75003> in <module>()
22 print(harry_potter_book.get_context_length())
23
---> 24 print(harry_potter_book.__context)
AttributeError: 'Document' object has no attribute '__context'
ご覧のとおり、クラスDocumentはDocumentクラスを定義し、さらに下に3つの関数があることがわかります。これらの3つの関数は、Documentクラスの3つの関数です。
その中で、initはコンストラクターを表します。これは、オブジェクトが生成されたときに自動的に呼び出される関数を意味します。コード行harry_potter_book = Document(...)が実行されると、文字列「initfunctioncalled」が出力されることがわかります。また、get_context_length()とintercept_context()はクラスの通常の関数であり、オブジェクトのプロパティを使用して何かを行うためにそれらを呼び出します。
クラスDocumentには、title、author、および__contextの3つの属性もあり、それぞれtitle、author、およびcontentを表し、コンストラクターを介して渡されます。ここのコードは非常に直感的で、intercept_contextがオブジェクトharry_potter_bookの__context属性を変更できることがわかります。
ここで強調する必要がある唯一のポイントは、属性が__で始まる場合(ここには2つの_があることに注意してください)、この属性をデフォルトでプライベート属性にすることです。プライベート属性とは、クラスの関数の外部でアクセスおよび変更されたくない属性を指します。したがって、ご覧のとおり、タイトルと作成者は自由に印刷できますが、print(harry_potter_book .__ context)はエラーを報告します。
高度なオブジェクト指向
エンジニアリングの実践では、複雑さが増し続けるにつれて、いくつかの問題を考えるかもしれません。
- 各オブジェクトが再構築せずにこれらの定数に簡単にアクセスできるように、クラスでいくつかの定数を定義するにはどうすればよいですか?
- 関数がこのクラスのプロパティを変更するためのアクセスを必要とせず、それをクラスの外に置くことが少し不適切である場合、どのようにそれをよりエレガントにすることができますか?
- クラスは類似したオブジェクトのコレクションですが、類似したクラスのコレクションにすることはできますか?
最初の2つの問題は簡単に解決できますが、一般的に使用されるコード仕様が含まれているため、ここにコード例を示します。
class Document():
WELCOME_STR = 'Welcome! The context for this book is {}.'
def __init__(self, title, author, context):
print('init function called')
self.title = title
self.author = author
self.__context = context
# 类函数
@classmethod
def create_empty_book(cls, title, author):
return cls(title=title, author=author, context='nothing')
# 成员函数
def get_context_length(self):
return len(self.__context)
# 静态函数
@staticmethod
def get_welcome(context):
return Document.WELCOME_STR.format(context)
empty_book = Document.create_empty_book('What Every Man Thinks About Apart from Sex', 'Professor Sheridan Simove')
print(empty_book.get_context_length())
print(empty_book.get_welcome('indeed nothing'))
########## 输出 ##########
init function called
7
Welcome! The context for this book is indeed nothing.
最初の質問は、Pythonクラスでは、これを実現するために、このコードのWELCOME_STRなど、関数と並行して値を宣言して割り当てるだけでよいということです。非常に一般的な方法は、定数を表すためにすべて大文字を使用することです。これにより、クラスでself.WELCOME_STRを使用するか、クラス外でEntity.WELCOME_STRを使用してこの文字列を表すことができます。
2番目の質問では、クラス関数、メンバー関数、静的関数の3つの概念を提案します。最初の2つの効果は動的であり、オブジェクトのプロパティにアクセスまたは変更できますが、静的関数はクラスとは関係ありません。最も明白な機能は、静的関数の最初のパラメーターが特別性はありません。
これらの関数を詳しく見てください。一般的に、静的関数を使用して、テストに便利なだけでなく、コード構造を最適化できる、いくつかの単純で独立したタスクを実行できます。静的関数は、関数の前の行に@staticmethodを追加することでも表すことができ、コードには対応する例があります。これは実際にはデコレータの概念を使用しています。これについては後の章で詳しく説明します。
クラス関数の最初のパラメーターは通常clsです。これは、クラスをに渡す必要があることを意味します。クラス関数で最も一般的に使用される関数は、さまざまなinitコンストラクターを実装することです。たとえば、上記のコードでは、create_empty_bookクラス関数を使用して新しいブックオブジェクトを作成し、そのコンテキストは「nothing」である必要があります。このようなコードは、直接の構成よりも明確です。同様に、クラス関数はデコレータ@classmethodで宣言する必要があります。
メンバー関数は、最も通常のクラス関数であり、デコレータ宣言は必要ありません。最初のパラメータselfは、現在のオブジェクトの参照を表します。この関数を使用して、目的のクエリ/変更クラス属性やその他の関数を実現できます。
継承
次に、3番目の質問を見てみましょう。クラスは類似したオブジェクトのコレクションなので、類似したクラスのコレクションにすることはできますか?
もちろん、その答えはです。十分に抽象化されている限り、クラスはあらゆるもののコレクションとして説明できます。
クラス継承とは、その名前が示すように、クラスが別のクラスの特性を持ち、別のクラスとは異なる固有の特性も持っているという事実を指します。ここでの最初のクラスはサブクラスと呼ばれ、もう1つは親クラスと呼ばれます。特性は実際にはクラスのプロパティと関数です。
class Entity():
def __init__(self, object_type):
print('parent class init called')
self.object_type = object_type
def get_context_length(self):
raise Exception('get_context_length not implemented')
def print_title(self):
print(self.title)
class Document(Entity):
def __init__(self, title, author, context):
print('Document class init called')
Entity.__init__(self, 'document')
self.title = title
self.author = author
self.__context = context
def get_context_length(self):
return len(self.__context)
class Video(Entity):
def __init__(self, title, author, video_length):
print('Video class init called')
Entity.__init__(self, 'video')
self.title = title
self.author = author
self.__video_length = video_length
def get_context_length(self):
return self.__video_length
harry_potter_book = Document('Harry Potter(Book)', 'J. K. Rowling', '... Forever Do not believe any thing is capable of thinking independently ...')
harry_potter_movie = Video('Harry Potter(Movie)', 'J. K. Rowling', 120)
print(harry_potter_book.object_type)
print(harry_potter_movie.object_type)
harry_potter_book.print_title()
harry_potter_movie.print_title()
print(harry_potter_book.get_context_length())
print(harry_potter_movie.get_context_length())
########## 输出 ##########
Document class init called
parent class init called
Video class init called
parent class init called
document
video
Harry Potter(Book)
Harry Potter(Movie)
77
120
また、コードを組み合わせてこれらの概念を学習します。このコードでは、ドキュメントとビデオは、タイトル、作成者、コンテンツなどの対応する属性を持っているという点で類似しています。Entityというクラスを2つの親クラスとして抽象化できます。
最初に注意するのはコンストラクターです。各クラスにはコンストラクタがあります。継承されたクラスがオブジェクトを生成するときに、親クラスのコンストラクタを自動的に呼び出すことはありません。したがって、init()関数で親クラスのコンストラクタを明示的に呼び出す必要があります。それらの実行順序は、子クラスのコンストラクター->親クラスのコンストラクターです。
次に、親クラスのget_context_length()関数に注意を払う必要があります。Entityを使用してオブジェクトを直接生成し、get_context_length()関数を呼び出すと、エラーが発生し、プログラムの実行が中断されます。これは実際には関数の書き換えと呼ばれる非常に優れた記述方法であるため、サブクラスはget_context_length()関数を書き換えて、元の関数を上書きする必要があります。
最後に、print_title()関数に注意する必要があります。この関数は親クラスで定義されていますが、サブクラスのオブジェクトはこれを使用して、抵抗なくタイトルを印刷できます。これは、継承の利点である反復コードの削減も反映しています。システムのエントロピー(つまり複雑さ)を減らします。
この時点で、継承についてより詳細に理解でき、オブジェクト指向プログラミングも開始されたと言えます。もちろん、より高いレベルに到達したい場合は、プログラミングの練習と詳細の学習が不可欠です。
最後に、抽象関数と抽象クラスを拡張したいと思います。また、説明に役立つコードを使用します。
from abc import ABCMeta, abstractmethod
class Entity(metaclass=ABCMeta):
@abstractmethod
def get_title(self):
pass
@abstractmethod
def set_title(self, title):
pass
class Document(Entity):
def get_title(self):
return self.title
def set_title(self, title):
self.title = title
document = Document()
document.set_title('Harry Potter')
print(document.get_title())
entity = Entity()
########## 输出 ##########
Harry Potter
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-7-266b2aa47bad> in <module>()
21 print(document.get_title())
22
---> 23 entity = Entity()
24 entity.set_title('Test')
TypeError: Can't instantiate abstract class Entity with abstract methods get_title, set_title
エンティティ自体は役に立たないことを発見したはずです。それを使用して、ドキュメントとビデオのいくつかの基本的な要素を定義するだけです。ただし、誤ってEntityオブジェクトを生成した場合はどうすればよいですか?このような手動エラーを防ぐために、抽象クラスを導入する必要があります。
抽象クラスは特別なクラスであり、親クラスとして生まれます。オブジェクト化されると、エラーが報告されます。同様に、抽象関数は抽象クラスで定義され、サブクラスは使用する前にこの関数をオーバーライドする必要があります。対応する抽象関数は、デコレータ@abstractmethodで表されます。
コード内のentity = Entity()はエラーを直接報告し、通常はDocumentを介してEntityを継承することによってのみ使用できることがわかります。
これは実際には、ソフトウェアエンジニアリングにおいて非常に重要な概念であり、インターフェイスを定義します。大規模なプロジェクトでは、多くの人が開発に協力する必要があります。たとえば、Facebookでは、アイデアが提案された後、開発チームと製品チームが最初に製品設計会議PM(製品マネージャー、製品マネージャー)を開催します。製品要件ドキュメントを作成してから繰り返します。TL(チームリーダー、プロジェクトマネージャー)は開発ドキュメントを作成します。開発ドキュメントは、さまざまなモジュールの一般的な機能とインターフェイス、各モジュール間のコラボレーション方法、ユニットテストと統合テスト、オンライングレースケールテストを定義します。 、監視およびロギングなど。開発プロセス。
抽象クラスはそのような存在であり、トップダウンの設計スタイルです。少量のコードを使用して、何をすべきかを明確に記述し、インターフェイスを定義してから、さまざまな開発者に渡して開発し、ドック。