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