Как я могу динамически создавать методы класса для класса в 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
, thesetattr()
вызова не произошло - вы не можете получить доступ к атрибутам класса из методов этого класса, вместо того, чтобы использовать просто
_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
и назвать его без каких-либо проблем, но до 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. Сделайте синтаксис менее подробным, мы используем Class decorator
теперь, если у нас есть MethodPatcher
decalred, мы должны сделать две вещи:
- определите дочерний класс
ChildClass
ofModelPatcher
, который содержит дополнительные методы, чтобы добавить - вызов
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')
вам нужно setattr(self, 'func', staticmethod(self._func))
вам нужно инициализировать класс variable=a()
вызов __init__
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'