我如何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.func,setattr()调用没有发生
-  你不能直接从该类的方法中访问一个类的属性,所以_func在__init__中使用self._func而需要使用self._func或self.__class__._funcself._funcself.__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.func的self参数将始终是最近创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.裹在一起
 因此,我们现在可以将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.最终解决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'