どのように私は動的にPythonでクラスのクラスメソッドを作成することができます



2 Answers

ここにいくつかの問題があります:

  • __init__は、インスタンスの作成時にのみ実行されますobj = a() 。 つまり、 a.funcを実行a.funcと、 setattr()コールは発生していません
  • そのクラスのメソッドからクラスの属性に直接アクセスすることはできません。したがって、 _func中で_funcだけを使うのではなく、 self._funcまたはself.__class__._func
  • selfaのインスタンスになります。インスタンスに属性を設定した場合、クラスのインスタンスではなくそのインスタンスに対してのみ属性を使用できます。 したがってsetattr(self, 'func', self._func)a.funcも、 a.funcはAttributeErrorを発生させます
  • staticmethodを使用すると、何もしなくてもstaticmethodは結果の関数を返しますが、引数は変更されません。 代わりに、あなたはsetattr(self, 'func', staticmethod(self._func))ようなものを望むでしょう(しかし、上記のコメントを考慮すると、これはまだ動作しません)

だから今、あなたは実際に何をしようとしているのですか? インスタンスを初期化するときに実際にクラスに属性を追加する場合は、次のようなことができます。

class a():
    def _func(self):
        return "asdf"

    def __init__(self, *args, **kwargs):
        setattr(self.__class__, 'func', staticmethod(self._func))

if __name__ == '__main__':
    obj = a()
    a.func
    a.func()

しかし、これはまだ奇妙なものです。 これで、 a.funcアクセスして問題なく呼び出すことができますが、 a.funcself引数は、常に最近作成されたa.funcインスタンスにaます。 _func()ようなインスタンスメソッドをクラスの静的メソッドまたはクラスメソッドに変換する方法はまったく問題ありません。

あなたはクラスに動的に関数を追加しようとしているので、おそらく次のようなものがあなたが実際にやろうとしているものに近いでしょうか?

class a():
    pass

def _func():
    return "asdf"

a.func = staticmethod(_func)  # or setattr(a, 'func', staticmethod(_func))

if __name__ == '__main__':
    a.func
    a.func()
Question

この質問には既に回答があります:

私が少しのPythonプログラムを

class a():
    def _func(self):
        return "asdf"

    # Not sure what to resplace __init__ with so that a.func will return asdf
    def __init__(self, *args, **kwargs):
         setattr(self, 'func', classmethod(self._func))

if __name__ == "__main__":
    a.func

私はトレースバックエラーを受け取ります

Traceback (most recent call last):
  File "setattr_static.py", line 9, in <module>
    a.func
AttributeError: class a has no attribute 'func'

私が把握しようとしているのは、オブジェクトをインスタンス化せずに、クラスメソッドをクラスに動的に設定するにはどうすればいいですか?

編集:

この問題に対する答えは、

class a():
    pass

def func(cls, some_other_argument):
    return some_other_argument

setattr(a, 'func', classmethod(func))

if __name__ == "__main__":
    print(a.func)
    print(a.func("asdf"))

次の出力を返します。

<bound method type.func of <class '__main__.a'>>
asdf



私はPython 2.7.5を使用しています。私は上記の解決策を手に入れることができませんでした。 これが私の結末です:

# define a class object (your class may be more complicated than this...)
class A(object):
    pass

def func(self):
    print 'I am class {}'.format(self.name)

A.func = func

# using classmethod() here failed with:
#       AttributeError: type object '...' has no attribute 'name'



1.基本的なアイデア:メソッドを保持するために余分なクラスを使用する

私は仕事をする意味のある方法を見つけました:

まず、このようなBaseClassを定義します。

class MethodPatcher:
    @classmethod
    def patch(cls, target):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(target, k, obj)

今私たちは元のクラスを持っています:

class MyClass(object):
    def a(self):
        print('a')

次に、新しいPatcherクラスに追加したい新しいメソッドを定義します。

(この場合、 _で始まるメソッド名を作成しないでください)

class MyPatcher(MethodPatcher):
    def b(self):
        print('b')

それから、

MyPatcher.patch(MyClass)

したがって、新しいメソッドb(self)が元のMyClass追加されています。

obj = MyClass()
obj.a()  # which prints an 'a'
obj.b()  # which prints a 'b'

2.構文をあまり冗長にし、クラスデコレータを使用する

MethodPatcher MethodPatcherがあれば、 MethodPatcher 2つのことを行う必要があります。

  • 追加する余分なメソッドを含むChildClassの子クラスChildClassを定義する
  • ChildClass.patch(TargetClass)呼び出す

だから私たちは、デコレータを使って2番目のステップを単純化できることをすぐに発見しました。

私たちはデコレータを定義します:

def patch_methods(model_class):
    def do_patch(cls):
        cls.patch(model_class)
    return do_patch

そして、私たちは次のように使うことができます:

@patch_methods(MyClass)
class MyClassPatcher(MethodPatcher):

    def extra_method_a(self):
        print('a', self)

    @classmethod
    def extra_class_method_b(cls):
        print('c', cls)

    # !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
    # calling this method on an instance will take the self into the first argument.
    # @staticmethod
    # def extra_static_method_c():
    #    print('c')

3.一緒にラップする

ですから、 MethodPatcherpatch_method定義を1つのモジュールに入れることができます:

# method_patcher.py

class MethodPatcher:
    @classmethod
    def patch(cls, target):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(target, k, obj)

def patch_methods(model_class):
    def do_patch(cls):
        cls.patch(model_class)
    return do_patch

だから私たちは自由に使うことができます:

from method_patcher import ModelPatcher, patch_model

4.最終的な解決策:もっと簡単な宣言

まもなく私はMethodPatcherクラスがMethodPatcherはないことを発見しました。 MethodPatcher @patch_methodデコレータは作業を行うことができるので、 patch_methodだけが必要patch_method

def patch_methods(model_class):
    def do_patch(cls):
        for k in cls.__dict__:
            obj = getattr(cls, k)
            if not k.startswith('_') and callable(obj):
                setattr(model_class, k, obj)
    return do_patch

使用方法は次のようになります。

@patch_methods(MyClass)
class MyClassPatcher:

    def extra_method_a(self):
        print('a', self)

    @classmethod
    def extra_class_method_b(cls):
        print('c', cls)

    # !!ATTENTION!! the effect on declaring staticmethod here may not work as expected:
    # calling this method on an instance will take the self into the first argument.
    # @staticmethod
    # def extra_static_method_c():
    #    print('c')



Related