如何在python [duplicate]中为类动态创建类方法

时间:2022-09-11 20:59:46

This question already has an answer here:

这个问题在这里已有答案:

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

6 个解决方案

#1


54  

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

#2


8  

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 do a.func, the setattr() call hasn't happened
  • __init__仅在您创建实例时运行,例如obj = a()。这意味着当你执行a.func时,没有发生setattr()调用
  • 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 use self._func or self.__class__._func
  • 你不能直接从该类的方法中访问类的属性,所以不需要在__init__中只使用_func,而是需要使用self._func或self .__ class __._ func
  • self will be an instance of a, if you set an attribute on the instance it will only be available for that instance, not for the class. So even after calling setattr(self, 'func', self._func), a.func will raise an AttributeError
  • self将是a的一个实例,如果在实例上设置一个属性,它只能用于该实例,而不能用于该类。所以即使在调用setattr(self,'func',self._func)之后,a.func也会引发一个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 like setattr(self, 'func', staticmethod(self._func)) (but taking into account the above comments, this still won't work)
  • 使用static方法不会做任何事情,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并毫无问题地调用它,但是a.func的self参数将始终是最近创建的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()

#3


2  

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

#4


1  

1. The basic idea: use an extra class to hold the methods

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)

(在这种情况下,不要使方法名称以_开头)

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

Now if we have the MethodPatcher decalred, we need to do two things:

现在,如果我们对MethodPatcher进行了decalred,我们需要做两件事:

  • define a child class ChildClass of ModelPatcher which contains the extra methods to add
  • 定义ModelPatcher的子类ChildClass,它包含要添加的额外方法
  • call ChildClass.patch(TargetClass)
  • 调用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

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

Soon I found that the MethodPatcher class is not nessesary, while the @patch_method decorator can do the work, so FINALLY we 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')

#5


1  

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

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

#6


0  

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'

#1


54  

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

#2


8  

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 do a.func, the setattr() call hasn't happened
  • __init__仅在您创建实例时运行,例如obj = a()。这意味着当你执行a.func时,没有发生setattr()调用
  • 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 use self._func or self.__class__._func
  • 你不能直接从该类的方法中访问类的属性,所以不需要在__init__中只使用_func,而是需要使用self._func或self .__ class __._ func
  • self will be an instance of a, if you set an attribute on the instance it will only be available for that instance, not for the class. So even after calling setattr(self, 'func', self._func), a.func will raise an AttributeError
  • self将是a的一个实例,如果在实例上设置一个属性,它只能用于该实例,而不能用于该类。所以即使在调用setattr(self,'func',self._func)之后,a.func也会引发一个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 like setattr(self, 'func', staticmethod(self._func)) (but taking into account the above comments, this still won't work)
  • 使用static方法不会做任何事情,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并毫无问题地调用它,但是a.func的self参数将始终是最近创建的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()

#3


2  

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

#4


1  

1. The basic idea: use an extra class to hold the methods

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)

(在这种情况下,不要使方法名称以_开头)

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

Now if we have the MethodPatcher decalred, we need to do two things:

现在,如果我们对MethodPatcher进行了decalred,我们需要做两件事:

  • define a child class ChildClass of ModelPatcher which contains the extra methods to add
  • 定义ModelPatcher的子类ChildClass,它包含要添加的额外方法
  • call ChildClass.patch(TargetClass)
  • 调用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

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

Soon I found that the MethodPatcher class is not nessesary, while the @patch_method decorator can do the work, so FINALLY we 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')

#5


1  

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

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

#6


0  

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'