Подтвердить что ты не робот

Как Python отличает функцию обратного вызова, которая является членом класса?

Пожалуйста, посмотрите на простой пример:

class A:
  def __init__(self, flag):
    self.flag = flag

  def func(self):
    print self.flag

a = A(1)
b = A(2)
callback_a = a.func
callback_b = b.func

callback_a()
callback_b()

Результат:

1
2

Он работает как ожидалось. Но у меня есть вопрос. В C функция обратного вызова передается как указатель. В Python он должен иметь аналогичный способ сделать это, поэтому вызывающий абонент знает адрес функции. Но в моем примере передается не только указатель функции, но и передается параметр (self), потому что тот же метод одного и того же класса печатает разные результаты. Поэтому мои вопросы:

  • Имеет ли такой метод в Python только один экземпляр в памяти? Я имею в виду, что код любого метода имеет только одну копию, и в моем примере метод не будет клонирован сам. Я думаю, что он должен иметь только одну копию, но здесь я все еще делаю этот вопрос, чтобы получить больше ресурсов.

  • Я помню, что все в Python является объектом. Итак, в моем примере есть два экземпляра функции с разными параметрами, но только одна копия кода?

4b9b3361

Ответ 1

Конкретно для CPython существует только одна копия объекта функции. Во время создания экземпляра класс обертывает несвязанные функции в своем пространстве имен как связанные методы. Но все они выполняют одну и ту же функцию.

Здесь ваш пример расширен, чтобы показать, что происходит.

class A(object):
  def __init__(self, flag):
    self.flag = flag

  def func(self):
    print self.flag

a = A(1)
b = A(2)

callback_a = a.func
callback_b = b.func

print "typeof(callback_a) = {0}".format(type(callback_a))
print "typeof(callback_b) = {0}".format(type(callback_b))

print "typeof(callback_a.__func__) = {0}".format(type(callback_a.__func__))
print "typeof(callback_b.__func__) = {0}".format(type(callback_b.__func__))

print "'callback_a.__func__ is callback_b.__func__'  is {0}".format(callback_a.__func__ is callback_b.__func__)

callback_a()
callback_b()

Этот код выводит

typeof(callback_a) = <type 'instancemethod'>
typeof(callback_b) = <type 'instancemethod'>
typeof(callback_a.__func__) = <type 'function'>
typeof(callback_b.__func__) = <type 'function'>
'callback_a.__func__ is callback_b.__func__'  is True

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

Ответ 2

В Python обратный вызов - это не просто ссылка на функцию-член. Вместо этого он "привязан" к объекту, к которому он относится, когда он был создан. Поэтому a.func создает вызываемый, привязанный к a, а b.func создает вызываемый, привязанный к b.

Python требует только одну реализацию func() в памяти, но она, вероятно, создаст одну или несколько "батутных" функций во время выполнения, чтобы выполнить привязку (я не уверен в внутренних деталях этого, и это будет отличаться между реализацией Python).

Если вы печатаете id(callback_a) и id(callback_b), вы получите разные результаты, показывая, что они действительно разные вызываемые объекты.