Правда ли, что "набор методов, тем не менее, фиксируется при первом определении класса"?

С прагматики языка программирования, Скотт

И Python, и Ruby более гибки, чем PHP или более традиционные object- ориентированные языки в отношении содержимого (членов) класса. Новые поля могут быть добавлены в объект Python, просто присваивая им: my_object.new_field = value. Однако набор методов фиксируется при первом определении класса. В Ruby только методы видны вне класса (методы "put" и "get" должны использоваться для доступа к полям), и все методы должны быть явно объявлены. Однако можно изменить существующее объявление класса, добавив или переопределив методы. Можно даже сделать это на основе объекта object-. В результате два объекта одного и того же класса могут не отображать одинаковое поведение.

Что означает "набор методов, однако, фиксируется при первом определении класса"?

Кажется, я нашел контрпример:

>>> class E:
...     pass
... 
>>> E.__dict__
mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'E' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'E' objects>})
>>> def myfun():
...     pass
... 
>>> E.mf=myfun
>>> E.__dict__
mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'E' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'E' objects>, 'mf': <function myfun at 0x7f6561daba60>})

Ответ 1

Как показано в вопросе: тривиально добавить функцию к объекту класса, который ведет себя точно так же, как любой метод:

def fake_method(self,idx):
    print(self, idx)

class MyClass(object):
    pass

MyClass.new_method = fake_method

n = MyClass()
n.new_method(10)
# <__main__.MyClass object at 0x000001BBA6E90860> 10

Вы также можете добавить экземпляр "вызываемых атрибутов" типа "method":

import types

def fake_method(self,idx):
    print(self, idx)

class MyClass(object):
    pass

n = MyClass()
n.new_method = types.MethodType(fake_method, n)

n.new_method(10)
# <__main__.MyClass object at 0x000001BBA6E9C2E8> 10

Здесь нужен types.MethodType, потому что в противном случае он будет вести себя как a staticmethod.

Мое резюме: Либо я пропущу какой-то критический момент цитаты, либо это неправильно.

Ответ 2

На уровне экземпляра можно напрямую изменять поля, как указано.

На уровне класса/модуля существует неофициальная механика под названием monkey patching. В основном вы получите ссылку (?) На класс/модуль и переопределите функцию.

Однако мне не ясно, могут ли быть отменены статические методы. И модификация действительна только в локальной области, где, как мне кажется, сделаны патчи для обезьян. Поэтому вы не должны ожидать, что другие сценарии импортируют один и тот же класс/модуль для совместного использования патчей обезьян, если они также не будут применять исправления явно. Конечно, вы также можете создать оберточный модуль.

Я не думаю, что сообщество Python сильно защищено. Трудно держать патчи в течение долгого времени.

Дополнительная информация: Что такое патч обезьяны?