Может ли установка Monkey заменить существующее определение функции в классе?

Я знаю, насколько жестко сообщество SO, поэтому постараюсь сделать все возможное, чтобы вопрос был минимальным, полным и проверяемым.

Я просто хочу знать, можно ли использовать исправление обезьяны для замены определения существующей функции?

Например:

class A():

    def foo():
       print '2'

def foo():
    print '5'

A.foo = foo

Этот способ, похоже, не работает также и в отношении того, почему я просто не добавляю новую функцию вместо замены существующей, я вызываю эти функции в других классах, и я понимаю, что исправление обезьяны добавляет эти функции во время выполнения и мне нужно, чтобы мой код Python запускался на сервере Apache Spark, который выдает ошибку, считая вызовы этой функции безотзывными.

Так что, пожалуйста, будьте любезны и помогите мне или предложите решение. Спасибо.

Изменить: Цель кода - вывести 5 при вызове A.foo.


person Vivek Shankar    schedule 17.03.2017    source источник
comment
Конечно, не с A.foo = print '5', вы пробовали A.foo = lambda: print('5')? (Обратите внимание, что с Python 2 вам нужно сделать from __future__ import print_function)   -  person tobias_k    schedule 17.03.2017
comment
Вы можете увидеть, как это делается, в этом ответе.   -  person Aran-Fey    schedule 17.03.2017
comment
Ваш код выглядит довольно искаженным. Вы хотите назначить новую функцию с именем foo A, когда вы вызываете foo?   -  person Sören Titze    schedule 17.03.2017
comment
@tobias_k Спасибо за быстрый ответ, не могли бы вы предложить что-нибудь для python 2?   -  person Vivek Shankar    schedule 17.03.2017
comment
@Nessuno Это то, что я намеревался донести. Короче, я хочу A.foo напечатать 5   -  person Vivek Shankar    schedule 17.03.2017
comment
@Rawing Ранее я уже отвечал на этот ответ, но я хочу назначать функцию не экземпляру, а самому классу, поэтому этот ответ на самом деле не относится к моему вопросу. Тем не менее, спасибо.   -  person Vivek Shankar    schedule 17.03.2017
comment
@tobias_k Не могли бы вы взглянуть на ссылку @Rawing. Неужели здесь нужно использовать lambda?   -  person Vivek Shankar    schedule 17.03.2017
comment
Приношу свои извинения, предыдущий пример был неправильным, но я все же собираюсь напечатать 5.   -  person Vivek Shankar    schedule 17.03.2017


Ответы (2)


Надеюсь, я понимаю, что вы здесь пытаетесь сделать. Это будет работать в Python 3:

class A():

  def foo():
     print('2')

def foo():
  A.foo = lambda: print('5')

A.foo() # Print '2'
foo()   # Assign the new method
A.foo() # Prints '5'

Однако в Python 2 есть несколько предостережений.

Итак, вы должны сделать это так:

from __future__ import print_function

class A():

  def foo():
    print('2')

def foo():
  A.foo = lambda: print('5')

A.foo.__func__() # Print '2'
foo()   # Assign the new method
A.foo.__func__() # Prints '5'

Изменить: увидев ваш вопрос в комментарии, я думаю, вы действительно хотите чего-то другого. Что это:

class A():

    def foo(self):
       print '2'

def foo(self):
  print '5'

a = A()
a.foo() # Print '2'
A.foo = foo   # Assign the new method
a.foo() # Prints '5'

Это прекрасно работает в Python 2.

self - это ссылка на текущий экземпляр, к которому привязан метод. Он не используется, когда вы просто вызываете что-то вроде print, которое обращается к каким-либо свойствам или методам, прикрепленным к этому экземпляру. Но для другого случая взгляните на следующий пример:

class A():

    msg = "Some message"

    def foo(self):
       print self.msg


def bar(self):
  self.msg = "Some other message"

a = A()
a.foo() # Print old msg
A.bar = bar   # Assign the new method
a.bar() # Assigns new message
a.foo() # Prints new message

Также, как отмечает Чепнер в комментарии под своим сообщением:

Имя я не особенное; это просто условность. Вы можете использовать это, если действительно хотите, и это не обязательно должно быть одно и то же имя в обеих функциях, определенных здесь. Важно то, что первым аргументом функции, используемой в качестве метода экземпляра, будет ссылка на вызывающий объект. a.foo () почти то же самое, что A.foo (a)

person Sören Titze    schedule 17.03.2017
comment
Да, это то, что @tobias_k предложил в комментариях, но лямбда становится немного неудобной при определении сложных функций. Я подписываюсь на это руководство, и оно похоже на мой пример в вопросе, работает ли это в python 3? Может быть, учебник написан на Python 3? Поскольку он явно не упоминается. - person Vivek Shankar; 17.03.2017
comment
@VivekShankar Это не имеет большого значения. Насколько я понимаю, в этом руководстве к классу добавляются методы bound, чтобы их можно было вызывать через a = new A(); a.foo(). Отличие в том, что они фактически предоставляют параметр self в определении метода. - person Sören Titze; 17.03.2017
comment
Согласен, но следуя способу, показанному в руководстве, и обычному патчу обезьяны, я получаю unresolved reference в PyCharm, когда делаю a = new A(), а затем называю его a.foo(). - person Vivek Shankar; 17.03.2017
comment
Извини, что напортачил. Это должно быть a = A(), пожалуйста, посмотрите мои последние изменения. - person Sören Titze; 17.03.2017
comment
Тот факт, что print является выражением в Python 2, просто означает, что вы не можете просто lambda выражение для определения функции. Вы по-прежнему можете использовать оператор def, чтобы обернуть print, и назначить эту функцию для A.foo. - person chepner; 17.03.2017
comment
@Nessuno Да, недавнее редактирование - это именно то, чего я хочу добиться, может объяснить, почему здесь важно добавление аргумента self к двум функциям? - person Vivek Shankar; 17.03.2017
comment
@chepner Да, ты прав. Просто он, казалось, пытался написать функцию класса, которая присоединяла бы несвязанный метод с тем же именем. Вот почему я спросил, действительно ли он хочет это сделать, в комментарии к своему вопросу. VivekShankar, пожалуйста, взгляните на последнее изменение. - person Sören Titze; 17.03.2017
comment
@Nessuno Только что посмотрел ваше последнее изменение, в котором все стало кристально чистым. Теперь, если мой экземпляр определен в другом файле python и ссылка на a.bar() дает неразрешенную ссылку, как я могу это исправить? - person Vivek Shankar; 17.03.2017
comment
Вы должны его импортировать. Посмотрите здесь: stackoverflow.com/questions/4142151/ - person Sören Titze; 17.03.2017
comment
@Nessuno Я еще не добавил пустой __init__.py, но я могу импортировать из этого каталога, и a = A() работает нормально, только когда я вызываю функцию, добавленную методом, который вы предложили выше, я получаю неразрешенную ошибку ссылки < / i> особенно в 'a.foo ()'. - person Vivek Shankar; 17.03.2017
comment
Я предполагаю, что вы переместили код, где вы на самом деле исправляете патч обезьяны, в другой файл. И этот файл никогда не запускается. Обратите внимание, что Python - это язык сценариев, что означает, что он не компилируется, как Java или C. Что, я думаю, происходит, так это то, что интерпретатор не имеет представления о методе, который вы пытаетесь добавить. Это означает, что вы можете либо сохранить все определения методов, которые вы пытаетесь добавить, в один и тот же файл, либо импортировать файл, включая функции. - person Sören Titze; 17.03.2017
comment
@Nessuno Спасибо, теперь я понимаю это намного лучше. - person Vivek Shankar; 17.03.2017

Ваша единственная проблема в том, что вы изначально неправильно определяете foo. Он должен принимать явный аргумент для вызывающего его экземпляра.

class A(object):
    def __init__(self)
        self.x = 2

    def foo(self):
        print(self.x)

def foo(this):
    print(this.x + 3)

A.foo = foo

a = A()
a.foo()  # outputs 5 in Python 2 and Python 3

В самом прямом смысле слова обезьяны патч - это то, как классы создаются в первую очередь. Оператор class - это почти синтаксический сахар для следующего кода:

def foo(self):
    print(self.x)
A = type('A', (object,), {'foo': foo})
del foo

Это не слишком упрощение, чтобы представить определение type как чего-то вроде

def type(name, bases, d):
    new_class = magic_function_to_make_a_class()
    new_class.name = name
    new_class.bases = bases
    for k, v in d.items():
        setattr(new_class, k, v)
    return new_class
person chepner    schedule 17.03.2017
comment
Да, у меня это сработало. Я новичок в Python, не могли бы вы объяснить, почему здесь так важен аргумент self? Судя по всему, у функций может быть даже разное количество параметров. - person Vivek Shankar; 17.03.2017
comment
Спасибо, теперь я понял self вроде как this в Python. - person Vivek Shankar; 17.03.2017
comment
Имя self не особенное; это просто условность. Вы можете использовать this, если действительно хотите, и это не обязательно должно быть одинаковое имя в обеих функциях, определенных здесь (я отредактирую ответ, чтобы продемонстрировать). Важно то, что первый аргумент функции, используемой в качестве метода экземпляра, будет ссылкой на вызывающий объект. a.foo() почти то же самое, что A.foo(a). - person chepner; 17.03.2017