如果我将一个小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'
我想弄清楚的是,如何在不实例化对象的情况下动态地将类方法设置为类?
这个问题的答案是
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
答案 0 :(得分:61)
您可以通过简单分配给类对象或通过类对象上的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))
答案 1 :(得分:8)
这里有几个问题:
__init__
仅在您创建实例时运行,例如obj = a()
。这意味着当您执行a.func
时,setattr()
来电未发生_func
或{{1}而不是仅在__init__
内使用self._func
}} self.__class__._func
将是self
的一个实例,如果您在实例上设置了一个属性,它只适用于该实例,而不适用于该类。因此,即使在调用a
之后,setattr(self, 'func', self._func)
也会引发AttributeError a.func
的方式将不会执行任何操作,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
的{{1}}参数将始终是最近创建的self
实例。我无法想到将a.func
之类的实例方法转换为类的静态方法或类方法的任何理智方法。
由于您正在尝试动态地向该类添加一个函数,或许类似下面的内容更接近您实际尝试的内容?
a
答案 2 :(得分:2)
你可以这样做
class a():
def _func(self):
return "asdf"
setattr(a, 'func', staticmethod(a._func))
if __name__ == "__main__":
a.func()
答案 3 :(得分:2)
我找到了一种有意义的工作方式:
首先,我们定义了这样一个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'
现在,如果我们有MethodPatcher
decalred,我们需要做两件事:
ChildClass
的子类ModelPatcher
,其中包含要添加的额外方法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')
因此,我们现在可以将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
很快我发现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
用法变为:
@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')
答案 4 :(得分:1)
您需要setattr(self, 'func', staticmethod(self._func))
您需要初始化课程variable=a()
才能调用__init__
静态类中没有init
答案 5 :(得分:0)
我使用的是Python 2.7.5,而且我无法让上述解决方案为我工作。这就是我最终的结果:
!pip install pandas-datareader