pythonのstaticmethodとclassmethod実装インスタンスコード

5910 ワード

本稿は,pythonのstaticmethod()というbuiltin手法の実現を明らかにしたいという好奇心から,いくつかの資料(主にpython公式マニュアル)を調べてここに集めた.
pythonクラスでは、methodを呼び出す方法は3つあります.普通のmethod、staticmethod、classmethodの2つはよく理解できるはずです.classmethodは、この関数を呼び出すときに、呼び出されたオブジェクトのclass objectオブジェクトを暗黙的に転送します.あれ?このclass objectはタイプではありませんか?No、pythonではclass objectは静的言語のようにタイプではなく、仮想マシンではオブジェクトです.普通のmethod呼び出しは自分のselfをパラメータとして渡す必要があり、初心者の頃はどうしても理解できませんでしたが、見すぎると自然に慣れてしまいます.不思議なことにstaticmethodとclassmethodは静的言語のようにキーワード定義を保持するのではなく、@staticmethodまたはstaticmethod()というbuiltin関数を使用して定義されます.この@staticmethodはいったい何ですか.

@staticmethod 
def foo(x): 
 print(x) 

前にjavaを使ったことがあるので、最初の反応はannotationですが...うーん、確かにAOPのようなもので、pythonではdecoratorと呼ばれています.もし私たちが自分でstaticmethodを実現するなら、どう書きますか?
公式のコードを研究して、私はまた変更して、このように書くべきだと感じました.

def foo(x): 
 print(x) 
class StaticMethod(object): 
 def __init__(self, function): 
  print("__init__() called") 
  self.f = function 
 def __get__(self, instance, owner): 
  print("\t__get__() called") 
  print("\tINFO: self = %s, instance =%s, owner = %s" % (self, instance, owner)) 
  return self.f 
 
class Class1(object): 
 method = StaticMethod(foo) 
  
if __name__ == '__main__': 
 ins = Class1() 
 print("ins = %s, Class1 = %s" % (ins, Class1)) 
 print("ins.method = %s, Class1.method = %s" % (ins.method, Class1.method)) 
 ins.method('abc') 
 Class1.method('xyz') 

出力結果は次のとおりです.
__init__() called ins = <__main__.class1 object=""at="">, Class1 = __get__() called INFO: self = <__main__.staticmethod object=""at="">, instance =<__main__.class1 object=""at="">, owner = __get__() called INFO: self = <__main__.staticmethod object=""at="">, instance =None, owner = ins.method = , Class1.method = __get__() called INFO: self = <__main__.staticmethod object=""at="">, instance =<__main__.class1 object=""at="">, owner = abc __get__() called INFO: self = <__main__.staticmethod object=""at="">, instance =None, owner = xyz
ええ、すべて順調に見えます.Class 1には変数methodが含まれていますが、このmethodも特殊に処理されたStaticMethodクラスです.このクラスには__get__関数があり、クラスが「get」されたときにアクセスされると、アクセス者のinstanceとclass情報がデフォルトで送信されます.だからmethod()という関数を呼び出すかどうかにかかわらず、methodにぶつかると、この関数がトリガーされ、現在のinstanceとclass情報が印刷されるのを見ました.insとClass 1のinstanceはそれぞれ異なりますが、__get__の関数ではfoo関数を返すだけなので、ここでmethodを呼び出すときは区別なく、同じfunctionオブジェクトを呼び出すことになります.
はい、ではclassmethodはどのように実現しますか?

def foo2(cls, x): 
 print("foo2's class = ", cls) 
 print(x) 
 
class ClassMethod(object): 
 def __init__(self, function): 
  print("ClassMethod: __init__() called") 
  self.f = function 
 def __get__(self, instance, owner = None): 
  print("\t__get__() called") 
  print("\tINFO: self = %s, instance =%s, owner = %s" % (self, instance, owner)) 
  def tmpfunc(x): 
   print("I'm tmpfunc") 
   return self.f(owner, x) 
  return tmpfunc 
 
class Class2(object): 
 method = ClassMethod(foo2) 
 
class Class21(Class2): 
 pass 
if __name__ == '__main__': 
 ins = Class2() 
 print("ins.method = %s, Class2.method = %s, Class21.method = %s" % (ins.method, Class2.method, Class21.method)) 
 ins.method('abc') 
 Class2.method('xyz') 
 Class21.method('asdf') 

出力結果は次のとおりです.
ClassMethod: __init__() called __get__() called INFO: self = <__main__.classmethod object=""at="">, instance =<__main__.class2 object=""at="">, owner = __get__() called INFO: self = <__main__.classmethod object=""at="">, instance =None, owner = __get__() called INFO: self = <__main__.classmethod object=""at="">, instance =None, owner = ins.method = , Class2.method = , Class21.method = __get__() called INFO: self = <__main__.classmethod object=""at="">, instance =<__main__.class2 object=""at="">, owner = I'm tmpfunc foo2's class = abc __get__() called INFO: self = <__main__.classmethod object=""at="">, instance =None, owner = I'm tmpfunc foo2's class = xyz __get__() called INFO: self = <__main__.classmethod object=""at="">, instance =None, owner = I'm tmpfunc foo2's class = asdf
classmethodとstaticmethodの実現方法は大同小異であることがわかる.staticmethodは比較的簡単でselfに直接戻る.f変数でよいがclassmethodではだめで,呼び出し時のclassタイプ情報をfoo 2関数に伝達する必要があり,この関数は受信したclass情報に基づいて異なる作業を行う.(でも今は何に使えるか考えていません)
注意すべき点があります.同志たちもさっき考えたかもしれませんが、私は必ずtempfuncを定義して、それに戻ってから仕事を完成しなければなりませんか.なくてもいいですか

def tmpfunc(x): 
   print("I'm tmpfunc") 
   return self.f(owner, x) 
  return tmpfunc 

直接1つ戻ります

return self.f(owner, *args) 
__get__が呼び出されたとき、まだパラメータが伝わっていなかったので、argsのデフォルトパラメータを直接伝えてはいけません.Class2.method('xyz')のパラメータは、return tmpfuncの後にのみtmpfuncに掛けられる.
もちろん、もし友達が成功したら、必ずXDを教えてください.
小結:staticmethodとclassmethodの実現は難しくないようですが、__get__関数のおかげです.前にも__get__が呼び出されたときにinstanceとclassの情報を記入するという話がありましたが、助かりました.しかし、この__get__関数はいったいどういうことなのか.どうしてそんなに不思議なの?Python中_を参考にしてみてくださいget__および_getattr__および_getattribute__の違い
まとめ
以上、pythonのstaticmethodとclassmethod実装インスタンスコードについてすべて説明したので、皆さんの役に立つことを願っています.興味のある方は引き続き当駅の他の関連テーマを参照することができます.不足点があれば、伝言を歓迎します.友达の本駅に対する支持に感谢します!