ホームページ バックエンド開発 Python チュートリアル Python のマジック メソッドを深く理解する

Python のマジック メソッドを深く理解する

Jun 16, 2016 am 08:43 AM
python 魔法の方法

私はしばらく Python に触れてきましたが、Python 関連のフレームワークやモジュールにもたくさん触れてきました。私がこれまで出会ってきた設計や実装について、皆さんと共有したいと思っています。より良いので、私は小さな「魅力的な Python」を取り上げました。これは私自身にとって良いスタートであり、皆さんが私を批判して修正してくれることを願っています。 :)

フラスコインポートリクエストから

Flask は非常に人気のある Python Web フレームワークで、著者も大小のプロジェクトを作成するためにこれを使用しました。それは、必要に応じてどこにいても使用できるという点です。現在のリクエストオブジェクトを取得するには、単純に次のようにします:

コードをコピー コードは次のとおりです:

フラスコインポートリクエストから

# 現在のリクエストからコンテンツを取得します
request.args
request.forms
request.cookie
... ...


とてもシンプルで覚えやすく、とても使いやすいです。ただし、その背後にある単純な実装は少し複雑です。 私の記事に従って謎を確認してください!

質問が 2 つありますか?

下を向く前に、まず 2 つの質問をします:

質問 1: request は静的クラス インスタンスのように見えます。なぜ、現在のリクエストの args 属性を取得するために、次のような式を使用する代わりに、 request.args のような式を直接使用できるのですか。

コードをコピー コードは次のとおりです:
フラスコインポート get_request
から # 現在のリクエストを取得します

request = get_request()
get_request().args

この方法はどうでしょうか? Flask はリクエストを現在のリクエスト オブジェクトにどのようにマップしますか?
質問 2: 実際の運用環境では、同じワーカー プロセスの下に多数のスレッド (またはコルーチン) が存在する可能性がありますが、そのような環境ではリクエスト クラスのインスタンスをどのように使用できますか?普通に働いているもの?

その秘密を知るには、flask のソースコードから始めるしかありません。

ソースコード、ソースコード、ソースコード

まず、flask のソース コードを開いて、最初の __init__.py からリクエストがどのように出力されるかを確認します。


コードをコピー コードは次のとおりです:
# ファイル: flask/__init__.py
from .globals import current_app、g、request、session、_request_ctx_stack

# ファイル: flask/globals.py
functools インポート部分から
werkzeug.local からインポート LocalStack、LocalProxy

def _lookup_req_object(名前):
トップ = _request_ctx_stack.top
最上位が None の場合:
raise RuntimeError('リクエストコンテキスト外で動作')
getattr(top, name) を返す
# 個のコンテキストローカル

_request_ctx_stack = LocalStack()

request = LocalProxy(partial(_lookup_req_object, 'request'))

flask のリクエストは globals.py から導入されていることがわかります。ここでリクエストを定義するコードは request = LocalProxy(partial(_lookup_req_object, 'request')) です。 , まずは授業についていく必要があり、まずは部分的に理解する必要があります。

しかし、partial(func, 'request') は別の関数を生成するために func の最初のデフォルトパラメータとして 'request' を使用するということは単純に理解できます。

したがって、partial(_lookup_req_object, 'request') は次のように理解できます。

呼び出し可能な関数を生成します。この関数は主に、スタックの先頭にある最初の RequestContext オブジェクトを _request_ctx_stack LocalStack オブジェクトから取得し、このオブジェクトのリクエスト属性を返します。

werkzeug の下にあるこの LocalProxy は私たちの注目を集めました。それが何であるかを見てみましょう:

コードをコピー コードは次のとおりです: @implements_bool
クラス LocalProxy(オブジェクト):
"""ローカル工場のプロキシとして機能します。すべての操作を
に転送します。 プロキシされたオブジェクト。転送でサポートされていない唯一の操作
。 は、右手オペランドおよび任意の種類の代入です。
... ...


紹介文の最初の数文を読むと、LocalProxy は主に Proxy、つまり werkzeug の Local オブジェクトにサービスを提供するプロキシとして機能することがわかります。それ自体に影響を与えるすべての操作を、それが表すオブジェクトに「転送」します。

それでは、このプロキシは Python を介してどのように実装されるのでしょうか?答えはソースコードにあります:


コードをコピー コードは次のとおりです: # 説明の便宜上、コードを一部削除・変更しました

@implements_bool クラス LocalProxy(オブジェクト):

__slots__ = ('__local', '__dict__', '__name__')

def __init__(self, local, name=None):
# ここで __setattr__ メソッドが渡される、self の
に注意する必要がある点が 1 つあります。 # "_LocalProxy__local" 属性はローカルに設定されています。興味があるかもしれません
# この属性名はなぜ奇妙なのでしょうか? 実際、これは Python が実際の
をサポートしていないためです。 # プライベートメンバー、詳細については公式ドキュメントを参照してください:
# http://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
# ここでは、self.__local = local :)
として扱う必要があるだけです。 object.__setattr__(self, '_LocalProxy__local', local)
object.__setattr__(self, '__name__', name)

def _get_current_object(self):
"""
得る パフォーマンス上の理由から、このエージェントの実際のオブジェクトを取得するか、それを別の
に使用する必要があります。 場所。
"""
# ここでの主な目的は、request
を分析するときに、プロキシ オブジェクトが werkzeug のローカル オブジェクトであるかどうかを判断することです。 このロジックは # の処理では使用されません。
そうでない場合は、attr(self.__local, '__release_local__'):
#localproxy(partial(_lookup_req_object、 'request'))によると)
# self.__local() メソッドを呼び出すと、partial(_lookup_req_object, 'request')()
を取得します。 # それは「_request_ctx_stack.top.request」
です return self.__local()
試してみてください:
return getattr(self.__local, self.__name__)
AttributeError を除く:
Raise RuntimeError('%s にバインドされたオブジェクトがありません' % self.__name__)

# 次は、Python マジック メソッドの長いリストです。ローカル プロキシは (ほぼ) すべての Python でオーバーロードされています。

#組み込みのマジック メソッド。すべての独自の操作が _get_current_object()
を指すようにします。 #返されるオブジェクトは実際のプロキシ オブジェクトです。

... ...

__setattr__ = ラムダ x, n, v: setattr(x._get_current_object(), n, v)
__delattr__ = ラムダ x, n: delattr(x._get_current_object(), n)
__str__ = ラムダ x: str(x._get_current_object())
__lt__ = ラムダ x, o: x._get_current_object()
__le__ = ラムダ x, o: x._get_current_object() __eq__ = ラムダ x, o: x._get_current_object() == o
__ne__ = ラムダ x, o: x._get_current_object() != o
__gt__ = ラムダ x, o: x._get_current_object() > o
__ge__ = ラムダ x, o: x._get_current_object() >= o
... ...

これで、記事の冒頭の 2 番目の質問に答えることができます。現在のリクエスト オブジェクトを取得するために get_request() などのメソッド呼び出しを使用する必要がない理由は、すべて LocalProxy によるものです。

LocalProxy は、カスタム マジック メソッドを通じてプロキシとして機能します。実際のリクエスト オブジェクトを指し、リクエストに対するすべての操作をプロキシします。

どうでしょうか。request.args が思ったほど単純ではないことがわかりました。

次に、2 番目の質問を見てみましょう。マルチスレッド環境ではリクエストはどのように動作するのでしょうか? globals.py に戻りましょう:


コードをコピー コードは次のとおりです:
functools インポート部分から
werkzeug.local からインポート LocalStack、LocalProxy


def _lookup_req_object(名前):
トップ = _request_ctx_stack.top
最上位が None の場合:
raise RuntimeError('リクエストコンテキスト外で動作')
getattr(top, name) を返す

# 個のコンテキストローカル

_request_ctx_stack = LocalStack()
request = LocalProxy(partial(_lookup_req_object, 'request'))

問題の鍵はこの _request_ctx_stack オブジェクトにあります。LocalStack のソース コードを見つけてください。

コードをコピー コードは次のとおりです:
クラス LocalStack(オブジェクト):
def __init__(self):

# 実際には、LocalStack は主に別の Local クラスを使用します
# その主要なメソッドの一部もこの Local クラスにプロキシされます
# Local クラスと比較して、プッシュ、ポップなどのスタック「スタック」関連のメソッドがさらに実装されています。
#したがって、ローカル コードを直接確認する必要があります
self._local = Local()

... ...

@プロパティ

def トップ(自分):
"""
スタックの一番上にあるオブジェクトを返します
"""
試してみてください:
return self._local.stack[-1]
(AttributeError、IndexError) を除く:
なしを返す

#したがって、_request_ctx_stack.top を呼び出すと、実際には _request_ctx_stack._local.stack[-1]
を呼び出します。 # Local クラスがどのように実装されているかを見てみましょう。その前に、
の下にある get_ident メソッドを見てみる必要があります。

# flask が gevent のようなコンテナーで実行されている場合は、まず greenlet から getcurrent メソッドをインポートしてみます

# すべてのリクエストは、スレッドではなくグリーンレットを最小単位として使用します。
試してみてください:
greenlet から getcurrent を get_ident
としてインポートします ImportError を除く:
試してみてください:
スレッドインポート get_ident
から ImportError を除く:
_thread import get_ident
から

# つまり、この get_ident メソッドは、リクエストごとに一意の現在のコルーチン/スレッド ID を返します

クラスローカル(オブジェクト):
__slots__ = ('__storage__', '__ident_func__')

def __init__(self):

object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident)

... ...

# 問題の鍵は、Local クラスが 2 つのマジック メソッド __getattr__ と __setattr__ をオーバーロードしていることです

def __getattr__(自分, 名前):

試してみてください:
# ここで、現在の一意の ID である self.__ident_func__() を呼び出します
# __storage__
のキーとして使用 return self.__storage__[self.__ident_func__()][name]
KeyError を除く:
Raise AttributeError(name)

def __setattr__(self, name, value):

ident = self.__ident_func__()
ストレージ = self.__storage__
試してみてください:
ストレージ[識別子][名前] = 値
KeyError を除く:
storage[ident] = {名前: 値}

... ...

# これら 2 つのマジック メソッドをオーバーロードした後

# Local().some_value は見た目ほど単純ではなくなりました:
# まず get_ident メソッドを呼び出して、現在実行中のスレッド/コルーチン ID を取得します
# 次に、次のように、この ID スペースの下の some_value 属性を取得します:
#
# Local().some_value -> Local()[current_thread_id()].some_value
#
# 属性を設定するときにも同じ原則が適用されます

これらの分析を通じて、現在のスレッド/コルーチン ID を使用し、いくつかのマジック メソッドをオーバーロードすることにより、Flask はさまざまなワーカー スレッドがスタック オブジェクトの独自の共有を使用できるようになります。これにより、リクエストが正常に動作することが保証されます。

現時点で、この記事はほぼ完成しています。ユーザーの利便性を考慮すると、フレームワークやツールの開発者は多くの追加の作業を行う必要があることがわかります。この点では、Python も非常に優れたサポートを備えています。

私たちがしなければならないのは、Python の魔法の部分を学びマスターし、魔法を使ってコードをより簡潔で使いやすくすることです。

ただし、魔法はまばゆいばかりですが、悪用しないでください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

C言語合計の機能は何ですか? C言語合計の機能は何ですか? Apr 03, 2025 pm 02:21 PM

C言語に組み込みの合計機能はないため、自分で書く必要があります。合計は、配列を通過して要素を蓄積することで達成できます。ループバージョン:合計は、ループとアレイの長さを使用して計算されます。ポインターバージョン:ポインターを使用してアレイ要素を指し示し、効率的な合計が自己概要ポインターを通じて達成されます。アレイバージョンを動的に割り当てます:[アレイ]を動的に割り当ててメモリを自分で管理し、メモリの漏れを防ぐために割り当てられたメモリが解放されます。

誰がより多くのPythonまたはJavaScriptを支払われますか? 誰がより多くのPythonまたはJavaScriptを支払われますか? Apr 04, 2025 am 12:09 AM

スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

独特の目標は関連していますか? 独特の目標は関連していますか? Apr 03, 2025 pm 10:30 PM

明確で明確なものは区別に関連していますが、それらは異なる方法で使用されます。明確な(形容詞)は、物事自体の独自性を説明し、物事の違いを強調するために使用されます。明確な(動詞)は、区別の動作または能力を表し、差別プロセスを説明するために使用されます。プログラミングでは、個別は、重複排除操作などのコレクション内の要素の独自性を表すためによく使用されます。明確なは、奇数や偶数の偶数を区別するなど、アルゴリズムまたは関数の設計に反映されます。最適化する場合、異なる操作は適切なアルゴリズムとデータ構造を選択する必要がありますが、異なる操作は、論理効率の区別を最適化し、明確で読み取り可能なコードの書き込みに注意を払う必要があります。

Cで理解する方法!x? Cで理解する方法!x? Apr 03, 2025 pm 02:33 PM

!X理解!Xは、C言語の論理的な非操作者です。 Xの値をブーリングします。つまり、虚偽の真の変化、trueへの誤った変更です。ただし、Cの真実と虚偽はブール型ではなく数値で表されていることに注意してください。非ゼロは真であると見なされ、0のみが偽と見なされます。したがって、!xは正の数と同じ負の数を扱い、真実と見なされます。

c言語でsumとはどういう意味ですか? c言語でsumとはどういう意味ですか? Apr 03, 2025 pm 02:36 PM

Cには組み込みの合計関数はありませんが、次のように実装できます。ループを使用して要素を1つずつ蓄積します。ポインターを使用して、要素に1つずつアクセスして蓄積します。大量のデータ量については、並列計算を検討してください。

H5ページの生産には継続的なメンテナンスが必要ですか? H5ページの生産には継続的なメンテナンスが必要ですか? Apr 05, 2025 pm 11:27 PM

H5ページは、コードの脆弱性、ブラウザー互換性、パフォーマンスの最適化、セキュリティの更新、ユーザーエクスペリエンスの改善などの要因のため、継続的に維持する必要があります。効果的なメンテナンス方法には、完全なテストシステムの確立、バージョン制御ツールの使用、定期的にページのパフォーマンスの監視、ユーザーフィードバックの収集、メンテナンス計画の策定が含まれます。

58.com作業ページでリアルタイムアプリケーションと視聴者のデータを取得する方法は? 58.com作業ページでリアルタイムアプリケーションと視聴者のデータを取得する方法は? Apr 05, 2025 am 08:06 AM

クロール中に58.com作業ページの動的データを取得するにはどうすればよいですか? Crawlerツールを使用して58.comの作業ページをrawったら、これに遭遇する可能性があります...

ラブコードのコピーをコピーして貼り付けて無料でラブコードを貼り付けます ラブコードのコピーをコピーして貼り付けて無料でラブコードを貼り付けます Apr 04, 2025 am 06:48 AM

コードのコピーと貼り付けは不可能ではありませんが、注意して扱う必要があります。コード内の環境、ライブラリ、バージョンなどの依存関係は、現在のプロジェクトと一致しないため、エラーや予測不可能な結果が得られます。ファイルパス、従属ライブラリ、Pythonバージョンなど、コンテキストが一貫していることを確認してください。さらに、特定のライブラリのコードをコピーして貼り付けるときは、ライブラリとその依存関係をインストールする必要がある場合があります。一般的なエラーには、パスエラー、バージョンの競合、一貫性のないコードスタイルが含まれます。パフォーマンスの最適化は、コードの元の目的と制約に従って再設計またはリファクタリングする必要があります。コピーされたコードを理解してデバッグすることが重要であり、盲目的にコピーして貼り付けないでください。

See all articles