web-dev-qa-db-ja.com

pythonでクラスのクラスメソッドを動的に作成するにはどうすればよいですか

少し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 __== "__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 __== "__main__":
    print(a.func)
    print(a.func("asdf"))

次の出力を返します

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

クラスオブジェクトへの単純な割り当てまたはクラスオブジェクトのsetattrによって、クラスにクラスメソッドを動的に追加できます。ここでは、混乱を減らすためにクラスが大文字で始まるというpython規則を使用しています。

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

# a class method takes the class object as its first variable
def func(cls):
    print 'I am a class method'

# you can just add it to the class if you already know the name you want to use
A.func = classmethod(func)

# or you can auto-generate the name and set it this way
the_name = 'other_func' 
setattr(A, the_name, classmethod(func))
63
tdelaney

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

  • ___init___は、インスタンスを作成するときにのみ実行されます。 obj = a()。これは、_a.func_を実行するときに、setattr()呼び出しが発生しなかったことを意味します
  • そのクラスのメソッド内からクラスの属性に直接アクセスすることはできません。そのため、__func_内で___init___を使用する代わりに、_self._func_または_self.__class__._func_を使用する必要があります。
  • selfaのインスタンスになります。インスタンスに属性を設定すると、クラスではなくそのインスタンスでのみ使用可能になります。したがって、setattr(self, 'func', self._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 __== '__main__':
    obj = a()
    a.func
    a.func()
_

ただし、これはまだ奇妙です。これで、_a.func_にアクセスして問題なく呼び出すことができますが、_a.func_のself引数は常に、最後に作成されたaのインスタンスになります。 _func()のようなインスタンスメソッドをクラスの静的メソッドまたはクラスメソッドに変える正しい方法を実際に考えることはできません。

クラスに関数を動的に追加しようとしているので、おそらく次のようなものが実際にしようとしていることに近くなりますか?

_class a():
    pass

def _func():
    return "asdf"

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

if __== '__main__':
    a.func
    a.func()
_
8
Andrew Clark

この方法でできます

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

setattr(a, 'func', staticmethod(a._func))

if __== "__main__":
    a.func()
2
eri

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がデカールされている場合、2つのことを行う必要があります。

  • 追加する追加のメソッドを含むChildClassModelPatcherの子クラスを定義します
  • 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.一緒に包む

したがって、MethodPatcherと_patch_method_の定義を単一のモジュールに入れることができます。

_# 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クラスは必須ではなく、_@patch_method_デコレーターが作業を行うことができるため、[〜#〜] finally [〜#〜]のみ_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')
_
2
Alfred Huang

setattr(self, 'func', staticmethod(self._func))する必要があります

___init___を呼び出すには、クラスvariable=a()を初期化する必要があります。静的クラスには初期化がありません

1
eri

私は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'
0
Cognitiaclaeves