2012-04-04 36 views
23

hãy nhìn vào ví dụ đơn giản:Python phân biệt hàm gọi lại là thành viên của một lớp như thế nào?

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() 

Kết quả là:

1 
2 

Nó chạy như mong đợi. Nhưng tôi có một câu hỏi. Trong C, hàm gọi lại được chuyển thành một con trỏ. Trong Python, cần có cách tương tự để thực hiện việc này, vì vậy người gọi biết địa chỉ của hàm. Nhưng trong ví dụ của tôi, không chỉ con trỏ hàm được truyền, mà còn tham số (self) được truyền, bởi vì cùng một phương thức của cùng một lớp in ra các kết quả khác nhau. Vì vậy, câu hỏi của tôi là:

  1. Phương thức như vậy trong Python chỉ có một bản sao trong bộ nhớ? Ý nghĩa của tôi là mã của bất kỳ phương thức nào chỉ có một bản sao, và trong ví dụ của tôi, phương thức này sẽ không được nhân bản. Tôi nghĩ rằng nó chỉ nên có một bản sao, nhưng ở đây tôi vẫn thực hiện câu hỏi này để có được nhiều đầu vào hơn.

  2. Tôi nhớ mọi thứ trong Python là một đối tượng. Vì vậy, trong ví dụ của tôi, có hai trường hợp chức năng với các thông số khác nhau nhưng chỉ có một bản sao của mã?

+1

Tôi nghĩ nó sẽ tạo ra sự đóng cửa? –

+0

Bạn nên sử dụng các lớp kiểu mới. Phân lớp mọi thứ từ 'đối tượng' hoặc thứ gì đó phân lớp nó. – aaronasterling

+1

@aaronasterling: Mặc dù tôi không chắc nó có quá nhiều tác động đối với câu hỏi trong tầm tay? – jdi

Trả lời

15

Cụ thể đối với CPython, chỉ có một bản sao của đối tượng hàm. Trong suốt quá trình tạo cá thể, lớp kết thúc các hàm không liên kết trong không gian tên của nó như các phương thức được ràng buộc. Nhưng tất cả chúng đều có cùng chức năng.

Đây là ví dụ của bạn được mở rộng để hiển thị những gì đang diễn ra.

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() 

Mã này ra

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 

Bạn có thể thấy rõ ràng, sử dụng toán tử is, rằng cả hai instancemethod lớp học được chia sẻ đối tượng chức năng tương tự.

+2

Tôi muốn biết lý do tại sao điều này đã được giảm giá. Nếu có điều gì đó khách quan sai, thì tôi muốn biết. – aaronasterling

+0

Những gì tôi thấy thực sự thú vị là "printback" callback_a này là callback_b 'là {0} "định dạng (callback_a là callback_b)' prints * false *. Đến từ Javascript và Perl tôi thấy hành vi này thực sự đáng ngạc nhiên. Có tài liệu Python nào nói nhiều hơn về nó không? –

+2

'callback_a là callback_b' là' False' bởi vì mỗi hàm là một 'instancemethod' kết thúc tốt cả hàm ** AND ** đối tượng để gọi phương thức đó. Cả hai chia sẻ cùng một '__func__' (như được hiển thị trong bài đầu tiên), nhưng mỗi có một đối tượng' self' khác nhau mà hàm đó được gọi. Có lý? – PfhorSlayer

17

Trong Python, gọi lại không chỉ đơn giản là tham chiếu đến hàm thành viên. Thay vào đó, nó được "ràng buộc" với đối tượng mà nó đề cập đến khi nó được tạo ra. Vì vậy, a.func tạo một số điện thoại có thể gọi được liên kết với ab.func tạo một cuộc gọi có thể gọi là b.

Python chỉ cần thực hiện func() trong bộ nhớ, nhưng có thể sẽ tạo một hoặc nhiều hàm "trampoline" trong thời gian chạy để thực hiện ràng buộc (tôi không chắc chắn về chi tiết nội bộ về điều này và nó sẽ khác nhau giữa Triển khai Python anyway).

Nếu bạn in id(callback_a)id(callback_b) bạn sẽ nhận được kết quả khác nhau, cho thấy rằng chúng thực sự là các đối tượng có thể gọi khác nhau.

+1

Và nếu bạn làm 'id (b.func) == id (a.func)' bạn thấy rằng chúng trỏ đến cùng một bộ nhớ – jdi

+0

@jdi: Bạn có thể cho tôi biết tại sao 'id (a.func)' cho kết quả khác mỗi lần? – mshsayem

+0

@mshsayem 'id' được thực hiện như là địa chỉ bộ nhớ của đối tượng trong CPython. Vì vậy, nó nhận được một vị trí khác nhau trong bộ nhớ mỗi khi chương trình chạy. – aaronasterling

Các vấn đề liên quan