Предисловие - примечание о совместимости: другие ответы могут работать только в Python 2 - этот ответ должен прекрасно работать в Python 2 и 3. Если вы пишете только Python 3, вы можете явно не наследовать от object
, но в противном случае код должен остаться прежним.
Добавление метода к существующему экземпляру объекта
Я читал, что в Python можно добавить метод к существующему объекту (например, не в определении класса).
Я понимаю, что это не всегда хорошее решение. Но как можно это сделать?
Да, это возможно - но не рекомендуется
Я не рекомендую это. Это плохая идея. Не делай этого.
Вот несколько причин:
- Вы будете добавлять связанный объект к каждому экземпляру, с которым вы это делаете. Если вы делаете это много, вы, вероятно, будете тратить много памяти. Связанные методы обычно создаются только на короткое время их вызова, и затем они перестают существовать, когда автоматически собирается мусор. Если вы сделаете это вручную, у вас будет привязка имени, ссылающаяся на связанный метод - что предотвратит его сборку мусора при использовании.
- Экземпляры объектов данного типа обычно имеют свои методы для всех объектов этого типа. Если вы добавите методы в другом месте, некоторые экземпляры будут иметь эти методы, а другие - нет. Программисты этого не ожидают, и вы рискуете нарушить правило малейшего удивления .
- Поскольку есть другие действительно веские причины не делать этого, вы также дадите себе плохую репутацию, если сделаете это.
Таким образом, я предлагаю вам не делать этого, если у вас нет действительно веской причины. Гораздо лучше определить правильный метод в определении класса или less , предпочтительно для непосредственного исправления класса напрямую, например:
Foo.sample_method = sample_method
Так как это поучительно, я покажу вам несколько способов сделать это.
Как это можно сделать
Вот некоторый установочный код. Нам нужно определение класса. Его можно импортировать, но это действительно не имеет значения.
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
Создать экземпляр:
foo = Foo()
Создайте метод для добавления к нему:
def sample_method(self, bar, baz):
print(bar + baz)
Method naught (0) - использовать метод дескриптора, __get__
Пунктирный поиск функций вызывает метод функции __get__
с экземпляром, привязывая объект к методу и создавая, таким образом, «связанный метод».
foo.sample_method = sample_method.__get__(foo)
и сейчас:
>>> foo.sample_method(1,2)
3
Метод первый - types.MethodType
Сначала импортируем типы, из которых мы получим конструктор метода:
import types
Теперь мы добавляем метод к экземпляру. Для этого нам требуется конструктор MethodType из модуля types
(который мы импортировали выше).
Сигнатура аргумента для types.MethodType: (function, instance, class)
:
foo.sample_method = types.MethodType(sample_method, foo, Foo)
и использование:
>>> foo.sample_method(1,2)
3
Метод второй: лексическое связывание
Сначала мы создаем функцию-обертку, которая связывает метод с экземпляром:
def bind(instance, method):
def binding_scope_fn(*args, **kwargs):
return method(instance, *args, **kwargs)
return binding_scope_fn
использование:
>>> foo.sample_method = bind(foo, sample_method)
>>> foo.sample_method(1,2)
3
Метод третий: functools.partial
Неполная функция применяет первый аргумент (ы) к функции (и, необязательно, ключевые аргументы), и может быть позже вызвана с остальными аргументами (и переопределяющими аргументы ключевого слова). Таким образом:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
Это имеет смысл, если учесть, что связанные методы являются частичными функциями экземпляра.
Несвязанная функция как атрибут объекта - почему это не работает:
Если мы попытаемся добавить sample_method таким же образом, как мы могли бы добавить его к классу, он не связан с экземпляром и не принимает неявное self в качестве первого аргумента.
>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)
Мы можем заставить несвязанную функцию работать, явно передавая экземпляр (или что-либо еще, поскольку этот метод фактически не использует переменную аргумента self
), но это не будет соответствовать ожидаемой сигнатуре других экземпляров (если мы исправляем этот экземпляр):
>>> foo.sample_method(foo, 1, 2)
3
Заключение
Теперь вы знаете несколько способов, которыми вы могли бы сделать это, но со всей серьезностью - не делайте этого.