もう一度言いましょう。Pythonは、動的型付けと動的バインディングを備えた高級プログラミング言語です。私はそれを強力で高レベルの動的言語として説明します。多くの開発者は、Pythonの明確な構文、適切に構造化されたモジュールとパッケージ、およびその非常に高い柔軟性と最新機能の範囲のためにPythonを愛しています。
Pythonでは、クラスを記述してそこからオブジェクトをインスタンス化する必要はありません。プロジェクトに複雑な構造が必要ない場合は、関数を作成するだけです。さらに良いことに、コードをまったく構造化せずに、いくつかの単純で迅速なタスクを実行するためのフラットなスクリプトを書くことができます。
同時に、Pythonは100%オブジェクト指向言語です。どのようだ?まあ、簡単に言えば、 Pythonのすべてがオブジェクトです。 関数はオブジェクトであり、ファーストクラスのオブジェクトです(それが意味するものは何でも)。関数がオブジェクトであるというこの事実は重要なので、覚えておいてください。
したがって、Pythonで簡単なスクリプトを作成することも、Pythonターミナルを開いて、そこでステートメントを実行することもできます(これはとても便利です!)。しかし同時に、複雑なフレームワーク、アプリケーション、ライブラリなどを作成することもできます。 Pythonでは多くのことができます。もちろん、いくつかの制限がありますが、それはこの記事のトピックではありません。
ただし、Pythonは非常に強力で柔軟性があるため、Pythonでプログラミングする場合はいくつかのルール(またはパターン)が必要です。それでは、パターンとは何か、そしてそれらがPythonとどのように関連しているかを見てみましょう。また、いくつかの重要なPythonデザインパターンの実装にも進みます。
どのプログラミング言語もパターンに適しています。実際、パターンは、特定のプログラミング言語のコンテキストで検討する必要があります。パターン、言語構文、および性質の両方が、プログラミングに制限を課します。言語構文と言語の性質(動的、関数型、オブジェクト指向など)に起因する制限は、それらの存在の背後にある理由と同様に、異なる可能性があります。パターンに起因する制限には理由があり、それらは意図的なものです。それがパターンの基本的な目標です。何かをする方法としない方法を教えてください。パターン、特にPythonデザインパターンについては後で説明します。
Pythonの哲学は、よく考えられたベストプラクティスのアイデアの上に構築されています。 Pythonは動的言語であり(私はすでに言ったのですか?)、そのため、数行のコードで多くの一般的なデザインパターンをすでに実装しているか、実装を容易にしています。一部のデザインパターンはPythonに組み込まれているため、知らないうちに使用します。言語の性質上、他のパターンは必要ありません。
例えば、 工場 は、新しいオブジェクトを作成し、インスタンス化ロジックをユーザーから隠すことを目的とした構造的なPythonデザインパターンです。ただし、Pythonでのオブジェクトの作成は設計上動的であるため、Factoryのような追加は必要ありません。もちろん、必要に応じて自由に実装できます。本当に役立つ場合もあるかもしれませんが、それは例外であり、標準ではありません。
Pythonの哲学の何がそんなに良いのですか?から始めましょう この (Pythonターミナルで調べてください):
> >> import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those!
これらは従来の意味でのパターンではないかもしれませんが、これらは最もエレガントで便利な方法でプログラミングへの「Pythonic」アプローチを定義するルールです。
コードの構造化に役立つPEP-8コードガイドラインもあります。もちろん、いくつかの適切な例外を除いて、それは私にとって必須です。ちなみに、これらの例外はPEP-8自体によって推奨されています。
しかし、最も重要なことは、いつ矛盾するかを知ることです。スタイルガイドが適用されない場合もあります。疑わしい場合は、最善の判断を下してください。他の例を見て、何が最もよく見えるかを決定します。そして、遠慮なく質問してください!
グーグル自然言語APIの例
PEP-8をZenof Python(PEP-PEP-20)と組み合わせると、読みやすく保守しやすいコードを作成するための完璧な基盤が得られます。デザインパターンを追加すれば、一貫性と進化可能性を備えたあらゆる種類のソフトウェアシステムを作成する準備が整います。
すべてはGangof Four(GOF)から始まります。慣れていない場合は、オンラインで簡単に検索してください GOF 。
デザインパターンは、よく知られている問題を解決する一般的な方法です。 2つの主要な原則は、GOFによって定義されたデザインパターンの基礎にあります。
Pythonプログラマーの観点から、これら2つの原則を詳しく見ていきましょう。
について考える ダックタイピング 。 Pythonでは、これらのインターフェイスに従ってインターフェイスとプログラムクラスを定義するのは好きではありませんか?しかし、私に聞いてください!これは、インターフェイスについて考えないという意味ではありません。実際、ダックタイピングでは常にそうしています。
悪名高いダックタイピングアプローチについて、このパラダイムにどのように適合するかを確認するために、いくつかの言葉を述べましょう。インターフェイスへのプログラムです。
コールオプション式の値アヒルのように見えて、アヒルのように鳴くなら、それはアヒルです! つぶやき
オブジェクトの性質を気にする必要はありません。オブジェクトが何であるかを気にする必要はありません。必要なことを実行できるかどうかを知りたいだけです(オブジェクトのインターフェイスにのみ関心があります)。
オブジェクトはいんちきすることができますか?だから、それをいんちきさせてください!
try: bird.quack() except AttributeError: self.lol()
アヒルのインターフェースを定義しましたか?番号!実装ではなくインターフェースにプログラムしましたか?はい!そして、私はこれがとても素晴らしいと思います。
Alex MartelliがPythonのデザインパターンに関する有名なプレゼンテーションで指摘しているように、 「アヒルにタイプを教えるのには時間がかかりますが、後で多くの作業を節約できます!」
さて、それは私が呼んでいるものです Pythonic 原理! 1つのクラス(または多くの場合、複数のクラス)を別のクラスでラップする場合と比較して、作成するクラス/サブクラスの数が少なくなっています。
これを行う代わりに:
class User(DbObject): pass
私たちはこのようなことをすることができます:
class User: _persist_methods = ['get', 'save', 'delete'] def __init__(self, persister): self._persister = persister def __getattr__(self, attribute): if attribute in self._persist_methods: return getattr(self._persister, attribute)
利点は明らかです。ラップされたクラスのどのメソッドを公開するかを制限できます。実行時にパーシスターインスタンスを注入できます!たとえば、今日はリレーショナルデータベースですが、明日は必要なインターフェースを備えたものなら何でもかまいません(これも厄介なアヒルです)。
構成はPythonにとってエレガントで自然です。
行動パターンには、オブジェクト間のコミュニケーション、オブジェクトがどのように相互作用し、特定のタスクを実行するかが含まれます。 GOFの原則によると、Pythonには合計11の動作パターンがあります。 責任の連鎖、コマンド、インタープリター、イテレーター、メディエーター、メメント、オブザーバー、状態、戦略、テンプレート、訪問者。
これらのパターンは非常に便利だと思いますが、これは他のパターングループがそうではないという意味ではありません。
イテレータはPythonに組み込まれています。これは、この言語の最も強力な特性の1つです。何年も前に、イテレータがPythonを素晴らしいものにしていることをどこかで読みましたが、これは今でも当てはまると思います。 Pythonのイテレータとジェネレータについて十分に学ぶと、この特定のPythonパターンについて必要なすべてを知ることができます。
このパターンは、それぞれがリクエストの特定の部分に対応するさまざまなメソッドを使用してリクエストを処理する方法を提供します。ご存知のとおり、優れたコードの最良の原則の1つは、 単一責任 原理。
すべてのコードは、1つだけのことを実行する必要があります。
この原則は、このデザインパターンに深く統合されています。
たとえば、一部のコンテンツをフィルタリングする場合は、さまざまなフィルターを実装できます。各フィルターは、正確で明確に定義された1つのタイプのフィルタリングを実行します。これらのフィルタは、不快な言葉、広告、不適切な動画コンテンツなどをフィルタリングするために使用できます。
class ContentFilter(object): def __init__(self, filters=None): self._filters = list() if filters is not None: self._filters += filters def filter(self, content): for filter in self._filters: content = filter(content) return content filter = ContentFilter([ offensive_filter, ads_filter, porno_video_filter]) filtered_content = filter.filter(content)
これは、私がプログラマーとして実装した最初のPythonデザインパターンの1つです。それは私に思い出させます: パターンは発明されていません、それらは発見されています 。それらは存在します。私たちはそれらを見つけて使用する必要があります。これは、何年も前に実装したすばらしいプロジェクトである、特別な目的のWYSIWYMXMLエディターで発見しました。このパターンをコードで集中的に使用した後、いくつかのサイトで詳細を読みました。
コマンドパターンは、何らかの理由で、実行するものを準備することから始めて、必要に応じて実行する必要がある場合に便利です。利点は、このような方法でアクションをカプセル化すると、 Python開発者 元に戻す/やり直し、アクションの履歴の保持など、実行されたアクションに関連する機能を追加します。
単純で頻繁に使用される例がどのように見えるかを見てみましょう。
class RenameFileCommand(object): def __init__(self, from_name, to_name): self._from = from_name self._to = to_name def execute(self): os.rename(self._from, self._to) def undo(self): os.rename(self._to, self._from) class History(object): def __init__(self): self._commands = list() def execute(self, command): self._commands.append(command) command.execute() def undo(self): self._commands.pop().undo() history = History() history.execute(RenameFileCommand('docs/cv.doc', 'docs/cv-en.doc')) history.execute(RenameFileCommand('docs/cv1.doc', 'docs/cv-bg.doc')) history.undo() history.undo()
まず、Pythonでは作成パターンが一般的に使用されていないことを指摘しましょう。どうして?言語の動的な性質のため。
クレジットカードハッカーソフトウェアの無料ダウンロード
FactoryはPythonに組み込まれていると私がかつて言ったよりも賢い人がいます。これは、言語自体が、十分にエレガントな方法でオブジェクトを作成するために必要なすべての柔軟性を提供することを意味します。 SingletonやFactoryなど、何かを上に実装する必要はめったにありません。
あるPythonデザインパターンのチュートリアルで、これらのデザインを説明する作成デザインパターンの説明を見つけました 「パターンは、オブジェクトを直接インスタンス化するのではなく、作成ロジックを非表示にしてオブジェクトを作成する方法を提供します。 新着 オペレーター。'
それは問題をかなり要約しています:私たちは 持っていない 新着 Pythonの演算子!
それでも、そのようなパターンを使用することで利点が得られると思われる場合は、いくつかを実装する方法を見てみましょう。
シングルトンパターンは、実行時に特定のクラスのインスタンスが1つだけ存在することを保証する場合に使用されます。 Pythonでこのパターンが本当に必要ですか?私の経験によると、シングルトンパターンを実装する代わりに、意図的に1つのインスタンスを作成し、それを使用する方が簡単です。
しかし、それを実装したい場合は、ここにいくつかの良いニュースがあります。Pythonでは、インスタンス化プロセスを(事実上他のものと一緒に)変更できます。 __new__()
を覚えておいてくださいさっき言った方法?さあ:
class Logger(object): def __new__(cls, *args, **kwargs): if not hasattr(cls, '_logger'): cls._logger = super(Logger, cls ).__new__(cls, *args, **kwargs) return cls._logger
この例では、 ログ シングルトンです。
これらは、Pythonでシングルトンを使用する代わりの方法です。
依存性注入がデザインパターンであるかどうかについて説明するつもりはありませんが、これは疎結合を実装するための非常に優れたメカニズムであり、アプリケーションの保守と拡張に役立ちます。ダックタイピングと組み合わせると、フォースがあなたと一緒になります。常に。
オブジェクトがいつ(またはさらに良いのはどこで)作成されるかという問題を扱っているので、この投稿の作成パターンのセクションにリストしました。外で作成されます。オブジェクトは、それらを使用する場所ではまったく作成されないため、依存関係は、それが消費される場所では作成されないということをお勧めします。コンシューマーコードは、外部で作成されたオブジェクトを受け取り、それを使用します。詳細については、このStackoverflowの質問に対する最も賛成の回答をお読みください。
これは、依存性注入の優れた説明であり、この特定の手法の可能性についての良いアイデアを提供します。基本的に、答えは次の例で問題を説明します。 自分で冷蔵庫から飲み物を手に入れるのではなく、代わりに必要性を述べてください。昼食と一緒に何か飲むものが必要だと両親に伝えてください。
Pythonは、それを簡単に実装するために必要なすべてを提供してくれます。 JavaやC#などの他の言語での実装の可能性について考えてみてください。そうすれば、Pythonの美しさにすぐに気付くでしょう。
依存性注入の簡単な例を考えてみましょう。
class Command: def __init__(self, authenticate=None, authorize=None): self.authenticate = authenticate or self._not_authenticated self.authorize = authorize or self._not_autorized def execute(self, user, action): self.authenticate(user) self.authorize(user, action) return action() if in_sudo_mode: command = Command(always_authenticated, always_authorized) else: command = Command(config.authenticate, config.authorize) command.execute(current_user, delete_user_action)
注入します オーセンティケーター そして 承認者 Commandクラスのメソッド。 Commandクラスに必要なのは、実装の詳細に煩わされることなく、それらを正常に実行することだけです。このようにして、実行時に使用することを決定した認証および承認メカニズムでCommandクラスを使用できます。
コンストラクターを介して依存性を注入する方法を示しましたが、オブジェクトのプロパティを直接設定することで簡単に依存性を注入でき、さらに多くの可能性を解き放ちます。
command = Command() if in_sudo_mode: command.authenticate = always_authenticated command.authorize = always_authorized else: command.authenticate = config.authenticate command.authorize = config.authorize command.execute(current_user, delete_user_action)
依存性注入について学ぶことはもっとたくさんあります。たとえば、好奇心旺盛な人々はIoCを検索します。
しかし、それを行う前に、別のStackoverflowの回答を読んでください。 この質問に最も賛成した人 。
繰り返しになりますが、Pythonでこの素晴らしいデザインパターンを実装することが、言語の組み込み機能を使用するだけの問題であることを示しました。
これが何を意味するかを忘れないでください。依存性注入手法により、非常に柔軟で簡単な単体テストが可能になります。オンザフライで保存されているデータを変更できるアーキテクチャを想像してみてください。データベースをモックするのは簡単な作業になりますね。詳細については、チェックアウトすることができます ApeeScapeによるPythonでのモックの紹介 。
あなたも研究したいかもしれません プロトタイプ 、 ビルダー そして 工場 デザインパターン。
これは、最も有名なPythonデザインパターンである可能性があります。
セレンのページオブジェクトモデル
かなりの数のオブジェクトを持つシステムがあると想像してください。すべてのオブジェクトは、豊富なAPIメソッドのセットを提供しています。このシステムで多くのことができますが、インターフェースを単純化するのはどうですか?すべてのAPIメソッドのよく考えられたサブセットを公開するインターフェイスオブジェクトを追加してみませんか? A ファサード!
Pythonファサードデザインパターンの例:
class Car(object): def __init__(self): self._tyres = [Tyre('front_left'), Tyre('front_right'), Tyre('rear_left'), Tyre('rear_right'), ] self._tank = Tank(70) def tyres_pressure(self): return [tyre.pressure for tyre in self._tyres] def fuel_level(self): return self._tank.level
驚きもトリックもありません、Car
クラスは ファサード 、そしてそれだけです。
場合 ファサード インターフェイスの簡素化に使用されます。 アダプター インターフェースの変更がすべてです。システムがアヒルを期待しているときに牛を使用するようなものです。
特定の宛先に情報を記録するための実用的な方法があるとします。あなたのメソッドは、宛先にwrite()
があることを想定しています。メソッド(たとえば、すべてのファイルオブジェクトにあります)。
def log(message, destination): destination.write('[{}] - {}'.format(datetime.now(), message))
これは、依存性注入を使用した適切に記述されたメソッドであり、優れた拡張性を実現します。ファイルではなくUDPソケットにログを記録したい場合、このUDPソケットを開く方法は知っていますが、唯一の問題はsocket
です。オブジェクトにはwrite()
がありません方法。あなたは アダプタ !
import socket class SocketWriter(object): def __init__(self, ip, port): self._socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self._ip = ip self._port = port def write(self, message): self._socket.send(message, (self._ip, self._port)) def log(message, destination): destination.write('[{}] - {}'.format(datetime.now(), message)) upd_logger = SocketWriter('1.2.3.4', '9999') log('Something happened', udp_destination)
しかし、なぜ私は見つけるのですか アダプタ ものすごく大事?まあ、それが依存性注入と効果的に組み合わされるとき、それは私たちに大きな柔軟性を与えます。新しいインターフェースをよく知られたインターフェースに変換するアダプターを実装できるのに、なぜ新しいインターフェースをサポートするように十分にテストされたコードを変更するのですか?
また、チェックアウトしてマスターする必要があります ブリッジ そして プロキシ との類似性によるデザインパターン アダプタ 。 Pythonでの実装がいかに簡単かを考え、プロジェクトでそれらを使用できるさまざまな方法を考えてください。
なんてラッキーなんだ! デコレータ 本当に素晴らしいです、そして私たちはすでにそれらを言語に統合しています。私がPythonで最も気に入っているのは、Pythonを使用することでベストプラクティスを使用する方法がわかることです。ベストプラクティス(特にデザインパターン)を意識する必要がないわけではありませんが、Pythonを使用すると、ベストプラクティスに従っているように感じます。個人的には、Pythonのベストプラクティスは直感的で第二の性質であり、これは初心者とエリート開発者の両方に高く評価されています。
ザ・ デコレータ パターンとは、追加機能を導入すること、特に継承を使用せずにそれを行うことです。
それでは、組み込みのPython機能を使用せずにメソッドを装飾する方法を確認しましょう。これは簡単な例です。
def execute(user, action): self.authenticate(user) self.authorize(user, action) return action()
ここでそれほど良くないのは、execute
です。関数は何かを実行するだけではありません。私たちは、この書簡に対する単一責任の原則に従っていません。
次のように書くとよいでしょう。
def execute(action): return action()
承認および認証機能は、別の場所、 デコレータ 、 そのようです:
web api.netチュートリアル
def execute(action, *args, **kwargs): return action() def autheticated_only(method): def decorated(*args, **kwargs): if check_authenticated(kwargs['user']): return method(*args, **kwargs) else: raise UnauthenticatedError return decorated def authorized_only(method): def decorated(*args, **kwargs): if check_authorized(kwargs['user'], kwargs['action']): return method(*args, **kwargs) else: raise UnauthorizeddError return decorated execute = authenticated_only(execute) execute = authorized_only(execute)
今execute()
方法は次のとおりです。
Pythonの統合デコレータ構文を使用して同じように記述します。
def autheticated_only(method): def decorated(*args, **kwargs): if check_authenticated(kwargs['user']): return method(*args, **kwargs ) else: raise UnauthenticatedError return decorated def authorized_only(method): def decorated(*args, **kwargs): if check_authorized(kwargs['user'], kwargs['action']): return method(*args, **kwargs) else: raise UnauthorizedError return decorated @authorized_only @authenticated_only def execute(action, *args, **kwargs): return action()
あなたがいることに注意することが重要です 機能に限定されない デコレータとして。デコレータには、クラス全体が含まれる場合があります。唯一の要件は、 呼び出し可能 。しかし、問題はありません。 __call__(self)
を定義する必要があります方法。
Pythonのfunctoolsモジュールを詳しく調べることもできます。そこには発見することがたくさんあります!
Pythonのデザインパターンを使用するのがいかに自然で簡単かを示しましたが、Pythonでのプログラミングも簡単に行えることも示しました。
「複雑なものよりも単純なものの方が優れています」 覚えてる?おそらく、デザインパターンのどれも完全かつ正式に記述されていないことに気づいたかもしれません。複雑な本格的な実装は示されていません。あなたは自分のスタイルとニーズに最も合う方法でそれらを「感じ」そして実行する必要があります。 Pythonは優れた言語であり、柔軟で再利用可能なコードを作成するために必要なすべての機能を提供します。
しかし、それはあなたにそれ以上のものを与えます。それはあなたに書くための「自由」を与えます すごく悪い コード。しないでください!自分を繰り返さないでください(DRY)。80文字を超えるコード行を記述しないでください。また、該当する場合はデザインパターンを使用することを忘れないでください。これは、他の人から学び、彼らの豊富な経験から無料で得るための最良の方法の1つです。