2012-04-04 2 views
23

Пожалуйста, смотрите на простом примере:Как 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), потому что тот же метод одного и того же класса печатает разные результаты. Итак, мои вопросы:

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

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

+1

Я бы подумал, что это создает закрытие? –

+0

Вы должны использовать классы newstyle. Подкласс - все, что угодно, от объекта или того, что подклассифицирует его. – aaronasterling

+1

@aaronasterling: Хотя я не уверен, что это слишком сильно повлияло на вопрос? – jdi

ответ

15

Специфично для 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

Хотелось бы узнать, почему это произошло. Если что-то объективно неправильно, то я хотел бы знать. – aaronasterling

+0

Что действительно интересно, так это то, что этот 'print '' callback_a является callback_b 'является {0}". Format (callback_a is callback_b) 'prints * false *. Исходя из Javascript и Perl Я нахожу это поведение действительно удивительным. Есть ли документы Python, которые говорят об этом больше? –

+2

'callback_a is callback_b' является' False', потому что каждый является 'instancemethod', который обертывает и функцию ** AND ** экземпляр, с помощью которого можно вызвать этот метод. Оба имеют один и тот же '__func__' (как показано в первоначальном сообщении), но каждый имеет другой экземпляр объекта' self', с которым эта функция вызывается. Имеют смысл? – PfhorSlayer

17

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

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

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

+1

И если вы делаете 'id (b.func) == id (a.func)', вы обнаружите, что они указывают на одну и ту же память – jdi

+0

@jdi: Можете ли вы сказать мне, почему 'id (a.func)' дает другой результат каждый раз? – mshsayem

+0

@mshsayem 'id' реализуется как адрес памяти объекта в CPython. Таким образом, каждый раз, когда программа запускается, она получает другое место в памяти. – aaronasterling

Смежные вопросы