如何在python中为类动态创建类方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17929543/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I dynamically create class methods for a class in python
提问by user1876508
If I define a little python program as
如果我将一个小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
I receive the traceback error
我收到回溯错误
Traceback (most recent call last):
File "setattr_static.py", line 9, in <module>
a.func
AttributeError: class a has no attribute 'func'
What I am trying to figure out is, how can I dynamically set a class method to a class without instantiating an object?
我想弄清楚的是,如何在不实例化对象的情况下将类方法动态设置为类?
Edit:
编辑:
The answer for this problem is
这个问题的答案是
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"))
returns the following output
返回以下输出
<bound method type.func of <class '__main__.a'>>
asdf
采纳答案by tdelaney
You can dynamically add a classmethod to a class by simple assignment to the class object or by setattr on the class object. Here I'm using the python convention that classes start with capital letters to reduce confusion:
您可以通过简单地分配给类对象或通过类对象上的 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))
回答by eri
You need to setattr(self, 'func', staticmethod(self._func))
你需要 setattr(self, 'func', staticmethod(self._func))
You need to initialize class variable=a()
to call __init__
There is no init in static class
您需要初始化类variable=a()
才能调用__init__
静态类中没有init
回答by eri
You can do it in this way
你可以这样做
class a():
def _func(self):
return "asdf"
setattr(a, 'func', staticmethod(a._func))
if __name__ == "__main__":
a.func()
回答by Andrew Clark
There are a couple of problems here:
这里有几个问题:
__init__
is only run when you create an instance, e.g.obj = a()
. This means that when you doa.func
, thesetattr()
call hasn't happened- You cannot access the attributes of a class directly from within methods of that class, so instead of using just
_func
inside of__init__
you would need to useself._func
orself.__class__._func
self
will be an instance ofa
, if you set an attribute on the instance it will only be available for that instance, not for the class. So even after callingsetattr(self, 'func', self._func)
,a.func
will raise an AttributeError- Using
staticmethod
the way you are will not do anything,staticmethod
will return a resulting function, it does not modify the argument. So instead you would want something likesetattr(self, 'func', staticmethod(self._func))
(but taking into account the above comments, this still won't work)
__init__
仅在您创建实例时运行,例如obj = a()
. 这意味着当你这样做时a.func
,setattr()
电话还没有发生- 你不能从类的方法中直接访问类的属性,所以不使用只是
_func
里面的__init__
,你需要使用self._func
或self.__class__._func
self
将是 的一个实例a
,如果您在该实例上设置了一个属性,它将仅适用于该实例,而不适用于该类。所以即使在调用之后setattr(self, 'func', self._func)
,a.func
也会引发一个 AttributeError- 使用
staticmethod
你的方式不会做任何事情,staticmethod
将返回一个结果函数,它不会修改参数。所以相反,你会想要类似的东西setattr(self, 'func', staticmethod(self._func))
(但考虑到上述评论,这仍然行不通)
So now the question is, what are you actually trying to do? If you really want to add an attribute to a class when initializing an instance, you could do something like the following:
所以现在的问题是,你到底想做什么?如果您真的想在初始化实例时向类添加属性,您可以执行以下操作:
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()
However, this is still kind of weird. Now you can access a.func
and call it without any problems, but the self
argument to a.func
will always be the most recently created instance of a
. I can't really think of any sane way to turn an instance method like _func()
into a static method or class method of the class.
不过,这还是有点奇怪。现在您可以a.func
毫无问题地访问和调用它,但 的self
参数a.func
将始终是最近创建的a
. 我真的想不出任何理智的方法将实例方法_func()
变成类的静态方法或类方法。
Since you are trying to dynamically add a function to the class, perhaps something like the following is closer to what you are actually trying to do?
由于您正在尝试向类动态添加一个函数,因此以下内容可能更接近您实际尝试执行的操作?
class a():
pass
def _func():
return "asdf"
a.func = staticmethod(_func) # or setattr(a, 'func', staticmethod(_func))
if __name__ == '__main__':
a.func
a.func()
回答by Alfred Huang
1. The basic idea: use an extra class to hold the methods
1. 基本思想:使用一个额外的类来保存方法
I found a meaningful way to do the work:
我找到了一种有意义的工作方式:
First, we define such a BaseClass:
首先,我们定义这样一个 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)
Now that we have an original class:
现在我们有了一个原始类:
class MyClass(object):
def a(self):
print('a')
Then we define the new method which we want to add on a new Patcher
class:
然后我们定义要添加到新Patcher
类的新方法:
(Do not make the method name starts with an _
in this case)
(_
在这种情况下,不要让方法名称以 an 开头)
class MyPatcher(MethodPatcher):
def b(self):
print('b')
Then call:
然后调用:
MyPatcher.patch(MyClass)
So, you'll find the new method b(self)
is added to the original MyClass
:
因此,您会发现新方法b(self)
已添加到原始方法中MyClass
:
obj = MyClass()
obj.a() # which prints an 'a'
obj.b() # which prints a 'b'
2. Make the syntax less verbose, we use class decorator
2.让语法不那么冗长,我们使用类装饰器
Now if we have the MethodPatcher
decalred, we need to do two things:
现在如果我们有MethodPatcher
贴花,我们需要做两件事:
- define a child class
ChildClass
ofModelPatcher
which contains the extra methods to add - call
ChildClass.patch(TargetClass)
- 定义一个子类
ChildClass
,ModelPatcher
其中包含要添加的额外方法 - 称呼
ChildClass.patch(TargetClass)
So we soon found that the second step can be simplified by using a decorator:
所以我们很快就发现可以通过使用装饰器来简化第二步:
We define a decorator:
我们定义一个装饰器:
def patch_methods(model_class):
def do_patch(cls):
cls.patch(model_class)
return do_patch
And we can use it like:
我们可以像这样使用它:
@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. Wrap together
3. 包起来
So, we can now put the definition of MethodPatcher
and patch_method
into a single module:
所以,我们现在可以把定义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
So we can use it freely:
所以我们可以自由使用它:
from method_patcher import ModelPatcher, patch_model
4. Final solution: More simple declaration
4.最终解决方案:更简单的声明
Soon I found that the MethodPatcher
class is not nessesary, while the @patch_method
decorator can do the work, so FINALLYwe only need a patch_method
:
很快我发现这个MethodPatcher
类不是必需的,而@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
And the usage becomes:
用法变成:
@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')
回答by Cognitiaclaeves
I'm using Python 2.7.5, and I wasn't able to get the above solutions working for me. This is what I ended up with:
我使用的是 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'