我如何dynamic创build一个类的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 __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' 

我想弄清楚的是,我怎样才能dynamic地将类方法设置为一个类,而不需要实例化一个对象呢?


编辑:

这个问题的答案是

 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 

您可以通过对类对象的简单赋值或对类对象的setattrdynamic添加classmethod到类。 在这里,我使用了以大写字母开头的python约定来减less混淆:

 # 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)) 

这里有几个问题:

  • __init__仅在创build实例时运行,例如obj = a() 。 这意味着当你执行a.funcsetattr()调用没有发生
  • 你不能直接从该类的方法中访问一个类的属性,所以_func__init__中使用self._func而需要使用self._funcself.__class__._func self._func self.__class__._func
  • self将是a的一个实例,如果你在实例上设置了一个属性,那么它只能用于该实例,而不能用于该类。 所以即使在调用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 __name__ == '__main__': obj = a() a.func a.func() 

但是,这仍然有点怪异。 现在,您可以访问a.func并且没有任何问题地调用它,但是a.funcself参数将始终是最近创build的a实例。 我真的不能想到任何将_func()这样的实例方法转换为类的静态方法或类方法的理智方法。

既然你正试图dynamic地为这个类添加一个函数,或许下面的东西更接近你实际正在做的事情?

 class a(): pass def _func(): return "asdf" a.func = staticmethod(_func) # or setattr(a, 'func', staticmethod(_func)) if __name__ == '__main__': a.func a.func() 

你可以这样做

 class a(): def _func(self): return "asdf" setattr(a, 'func', staticmethod(a._func)) if __name__ == "__main__": a.func() 

你需要setattr(self,'func',staticmethod(self._func))

你需要初始化class variable=a()来调用__init__静态类没有init

1.基本思想:用一个额外的类来保存这些方法

我find了一个有意义的方式来完成这项工作:

首先,我们定义这样一个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.减less语法冗长,我们使用类装饰器

现在,如果我们有了MethodPatcher ,我们需要做两件事情:

  • 定义一个包含要添加的额外方法的ModelPatcher的子类ChildClass
  • 调用ChildClass.patch(TargetClass)

所以我们很快发现使用装饰器可以简化第二步:

我们定义一个装饰器:

 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的定义放入一个模块中:

 # 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.最终解决scheme:更简单的声明

不久,我发现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') 

我正在使用Python 2.7.5,我无法得到上述解决scheme为我工作。 这是我结束了:

 # 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'