Подтвердить что ты не робот

Как я могу динамически создавать методы класса для класса в 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'

Я пытаюсь выяснить, как я могу динамически установить метод класса для класса без создания экземпляра объекта?


Изменить:

Ответ на эту проблему:

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
4b9b3361

Ответ 1

Вы можете динамически добавлять класс класса к классу простым назначением объекту класса или 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

Здесь есть несколько проблем:

  • __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)) (но с учетом вышеприведенных комментариев это все равно не сработает)

Итак, теперь вопрос в том, что вы на самом деле пытаетесь сделать? Если вы действительно хотите добавить атрибут к классу при инициализации экземпляра, вы можете сделать что-то вроде следующего:

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 и вызвать его без каких-либо проблем, но аргумент self для a.func всегда будет последним созданным экземпляром a. Я не могу представить себе разумный способ превратить метод экземпляра типа _func() в статический метод или метод класса этого класса.

Поскольку вы пытаетесь динамически добавлять функцию в класс, возможно, что-то вроде следующего ближе к тому, что вы на самом деле пытаетесь сделать?

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

Вы можете сделать это таким образом

class a():
    def _func(self):
        return "asdf"

setattr(a, 'func', staticmethod(a._func))

if __name__ == "__main__":
    a.func()

Ответ 4

Вам нужно установить setattr (self, 'func', staticmethod (self._func))

Вам нужно инициализировать класс variable=a() для вызова __init__ В статическом классе нет init.

Ответ 5

1. Основная идея: использовать дополнительный класс для хранения методов

Я нашел осмысленный способ выполнения работы:

Сначала мы определяем такой 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. Сделайте синтаксис менее подробным, мы используем декоратор класса

Теперь, если у нас есть 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')

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. Окончательное решение: более простая декларация

Вскоре я обнаружил, что класс 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')

Ответ 6

Я использую 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'