[Python]クロージャの実装におけるPythonとjsの違いを一度学ぶことを忘れないでください

私はかつてPythonで小さなことを書いたことがあります

しかし、最近、内部のインターフェースが突然失敗しました。
私がウェブサイトにアクセスしたとき、彼らはインターフェースを直接オフにし、機能はWeChat公式アカウントに移行されました。

その後、率いた同級生は、彼率いる本能に基づいてwxpyというライブラリを直接見つけまし

pip install wxpy

wxpy→ドキュメントアドレス

元のコードでは、この関数は完全にカプセル化されているため、彼がしなければならないのは、カプセル化されたインターフェイスを置き換えるだけで、それを引き続き楽しく使用できます。
しかし、私が予期していなかったことは、WeChatを使用してそれを達成しようとすること、以前の直接パケットget大きく異なることです。

  • クローラーを使用して、他のインターフェースを直接調整します。入力→インターフェースの内部処理→出力リターン、どちらの端であっても、それはプロセス全体です。

  • WeChatを使用して、メッセージの入力と送信→インターフェイスの内部処理(応答を待機)→出力の戻りを実現します。これは非常に異なり、問題は応答を待機することにあります。

入力→処理→出力をカプセル化された関数とすると、クローラーのクロールは同期プロセスですが、WeChatでのメッセージの送受信は非同期プロセスであるため、相手からの応答を待つ必要があります。

一流の同級生はそれを解決できなかったので、彼は私に来ました。
書類を見つけて簡単に読んだのですが、クラスメートの話を聞いてすぐにどうしたらいいかわかりました。

msg_received = False # 是否收到消息的状态标志
msg = '' # 消息

@bot.register(chat_obj) # 这里相当于为一个聊天对象注册了一个回调函数,当收到来自它的消息时回调
def getMsg(in_msg):
    msg_ = in_msg
    msg_received = True # 改变状态标志

def query(question):
    chat_obj.send_msg(question)
    while msg_received != True:
        pass # 等待,直到状态标志改变
    msg_received = False
    return  msg

もちろん、このように記述してもかまいません。分離されたパーツをつなぎ合わせるのは自然なことですが、エレガントな大学生として、コードでこのような結合度の低下をどのように許容できますか。
インターフェイスはエレガントでなければなりません


今、私たちはトピックを入力しようとしています

だから私は達成するためにクロージャを使用しようとしました:

def initial(chat_obj):
	msg_received = False # 是否收到消息的状态标志
	msg = '' # 消息
	
	@bot.register(chat_obj) # 这里相当于为一个聊天对象注册了一个回调函数,当收到来自它的消息时回调
	def getMsg(in_msg):
	    msg_ = in_msg
	    msg_received = True # 改变状态标志
	
	def query(question):
	    chat_obj.send_msg(question)
	    while msg_received != True:
	        pass # 等待,直到状态标志改变
	    msg_received = False
	    return  msg
    return main_fun

このロジックがjsで実装されている場合、問題はありません。
ただし、これはPythonであり、例外が発生します。

NameError:名前 'msg_received'が定義されていません

主な理由は、Pythonの基盤が貧弱で、体系的に学習していないことです。毎日のチューニングなどです。
何か問題が発生した場合は、それをチェックして今すぐ学習してください。
それはどれほど悪いですか?少し前に、私はトライが何のためにあるのかを学びました。

この例外を振り返ると、彼はクエリ関数のmsg_receivedが定義されていないと述べました。
疑問符がいっぱいですが、外側のスコープで探してみませんか?
不完全な基本的なPython構文を単純にめくった後、グローバルについて考えたので、関数の先頭でmsgとmsg_receivedをglobalで宣言しました。
もちろん、結果はまだ間違っていますが、私がそれについて考えると、問題を解決するという私の考えは正しいですが、私の文法の知識は十分ではありません。
それで情報を探し始めて、探したらこのブログに目を向けました→ https://www.jianshu.com/p/17a9d8584530
とても良かったし、問題の解決方法もとても簡単でした。関数の先頭で宣言するには、nolocalを使用します。変数は「非ローカル変数」で問題ありません。
このとき、jsを書くことに慣れていたとき、Pythonの可変スコープメカニズムがjsのそれとは少し違うことに気づきました。


Pythonとjsのクロージャ

簡単に言え何もしなければ、Pythonは、jsの反対である外部スコープから検索するのではなく、ローカル変数の定義を優先します

Pythonコードの一部を見てください:

def fun():
    b = 1
    def getb():
        return b
    return getb

getb = fun()
print(getb())

出力:

1

非常に正常ですよね?

次に、Pythonコードの一部を見てみましょう。

def fun():
    b = 1
    def getb():
        b += 1
        return b
    return getb

getb = fun()
print(getb())

エラー

UnboundLocalError:割り当て前に参照されたローカル変数 'b'

彼が意味したのは、bを使用する前に値を割り当てなかったということです。
冒頭で述べたように、Pythonはローカル変数の定義を優先します。bが左辺値になると、Pythonインタープリターはそれをローカル変数として直接使用します。
したがって、bに値を割り当てるときに、式でbを参照することはできません。


Pythonコードの一部を見てみましょう:

def fun():
    b = 1
    def getb():
        b = 2
        return b
    return getb

getb = fun()
print(getb())

出力:

2

この例の目的は、出力2が外部スコープのbではなく、getb関数のローカル変数bであることを示して思い出させることです。


したがって、正しい記述は次のようになります。
def fun():
    b = 1
    def getb():
    	nonlocal b
        b += 1
        return b
    return getb

getb = fun()
print(getb())

Pythonとは異なり、jsでは、インタプリタは最初に外部スコープでそれを探します

Pythonでのこのアプローチは、非常に悪いプログラミング習慣を助長していると思います。いかなる状況でも、外部スコープと同じ名前の変数をクロージャ関数で宣言しないでください。
jsでは、クロージャ関数が外部スコープの変数と同じ意味を持つローカル変数を使用する必要がある場合、区別のためにlocal_プレフィックスをプレフィックスとして付けます。


だからそれでいい。

おすすめ

転載: blog.csdn.net/qq_16181837/article/details/104805151