我如何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__._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.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'