Как я могу динамически создавать методы класса для класса в 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'
то, что я пытаюсь выяснить, как я могу динамически установить метод класса класса без создания экземпляра объекта?
Edit:
ответ на эту проблему
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
6 ответов:
вы можете динамически добавить classmethod к классу простым назначением объекту класса или 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))
есть несколько проблем:
__init__работает только при создании экземпляра, например,obj = a(). Это означает, что когда вы делаетеa.funcнаsetattr()звонка не было- вы не можете получить доступ к атрибутам класса из методов этого класса, вместо того, чтобы использовать просто
_funcвнутри__init__вам нужно будет использоватьself._funcилиself.__class__._funcselfбудет экземпляр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и назвать его без каких-либо проблем, но до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()
вы можете сделать это таким образом
class a(): def _func(self): return "asdf" setattr(a, 'func', staticmethod(a._func)) if __name__ == "__main__": a.func()
1. Основная идея: используйте дополнительный класс для хранения методов
я нашел осмысленный способ сделать работу:
во-первых, мы определяем такой базовый класс:
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. Сделайте синтаксис менее подробным, мы используем класс decorator
теперь, если у нас есть
MethodPatcherdecalred, мы должны сделать две вещи:
- определить дочерний класс
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_model4. Окончательное решение: более простое объявление
вскоре я обнаружил, что
MethodPatcherкласс не nessesary, в то время как@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')
вам нужно
setattr(self, 'func', staticmethod(self._func))вам нужно инициализировать класс
variable=a()вызов__init__Нет инициализации в статическом классе
Я использую 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'
Comments