2015-07-23 21 views
16

Trong câu hỏi trước, trong nhận xét, tôi đã biết rằng trong python __code__ atrribute của hàm có thể thay đổi. Do đó tôi có thể viết mã như sauTại sao __code__ cho hàm (Python) có thể thay đổi

def foo(): 
    print "Hello" 

def foo2(): 
    print "Hello 2" 

foo() 
foo.__code__ = foo2.__code__ 
foo() 

Output

Hello 
Hello 2 

tôi đã cố gắng googling, nhưng một trong hai vì không có thông tin (Tôi rất nghi ngờ điều này), hoặc từ khóa (__code__) là không thể tìm kiếm một cách dễ dàng , Tôi không thể tìm thấy một trường hợp sử dụng cho việc này.

Nó không có vẻ như "bởi vì hầu hết mọi thứ bằng Python là có thể thay đổi" là một câu trả lời hợp lý, hoặc, bởi vì thuộc tính khác của chức năng - __closure____globals__ - được một cách rõ ràng read-only (từ Objects/funcobject.c):

static PyMemberDef func_memberlist[] = { 
    {"__closure__", T_OBJECT,  OFF(func_closure), 
    RESTRICTED|READONLY}, 
    {"__doc__",  T_OBJECT,  OFF(func_doc), PY_WRITE_RESTRICTED}, 
    {"__globals__", T_OBJECT,  OFF(func_globals), 
    RESTRICTED|READONLY}, 
    {"__module__", T_OBJECT,  OFF(func_module), PY_WRITE_RESTRICTED}, 
    {NULL} /* Sentinel */ 
}; 

Tại sao __code__ có thể ghi trong khi các thuộc tính khác là chỉ đọc?

+1

Bạn không thể ngăn chặn bất kỳ loại 'bất chính' này bằng Python, '__code__' hay không. Hoặc tin tưởng các mô-đun được sử dụng hoặc không sử dụng chúng. – user2864740

+1

Có. Tôi đã thấy rằng thường là như vậy. Nhưng vẫn nghĩ để hỏi. –

+2

Thực hành ngăn xếp an toàn thay vì móc vào các API lạ. – TigerhawkT3

Trả lời

4

Thực tế là, hầu hết mọi thứ trong Python có thể thay đổi. Vì vậy, câu hỏi thực sự là, tại sao là __closure____globals__không phải?

Câu trả lời ban đầu xuất hiện đơn giản. Cả hai thứ này đều là các thùng chứa cho các biến mà hàm có thể cần. Bản thân đối tượng mã không mang các biến khép kín và toàn cầu của nó xung quanh nó; nó chỉ đơn thuần là biết làm thế nào để có được chúng từ chức năng. Nó lấy các giá trị thực tế ra khỏi hai thuộc tính này khi hàm được gọi.

Nhưng phạm vi chính chúng có thể thay đổi được, vì vậy câu trả lời này không thỏa mãn. Chúng ta cần phải giải thích tại sao việc sửa đổi những thứ này đặc biệt sẽ phá vỡ mọi thứ.

Đối với __closure__, chúng tôi có thể xem xét cấu trúc của nó. Nó không phải là một bản đồ, mà là một tuple của các tế bào. Nó không biết tên của các biến đóng trên. Khi đối tượng mã tìm kiếm một biến đóng, nó cần biết vị trí của nó trong bộ dữ liệu; chúng phù hợp với từng người một với co_freevars cũng là chỉ đọc. Và nếu tuple có kích thước sai hoặc không phải là một tuple ở tất cả, cơ chế này bị phá vỡ, có lẽ dữ dội (đọc: segfaults) nếu mã C cơ bản không mong đợi một tình huống như vậy. Buộc mã C để kiểm tra kiểu và kích cỡ của tuple là công việc bận rộn không cần thiết có thể được loại bỏ bằng cách làm cho thuộc tính chỉ đọc. Nếu bạn cố gắng thay thế __code__ bằng cách sử dụng một số biến số miễn phí khác, you get an error, vì vậy kích thước luôn đúng.

Đối với __globals__, giải thích ít rõ ràng hơn, nhưng tôi sẽ suy đoán. Cơ chế tra cứu phạm vi dự kiến ​​sẽ có quyền truy cập vào không gian tên chung tại mọi thời điểm. Thật vậy, bytecode có thể là hard-coded để đi thẳng đến không gian tên chung, nếu trình biên dịch có thể chứng minh không có không gian tên nào khác sẽ có một biến với một tên cụ thể. Nếu không gian tên toàn cục đột nhiên là None hoặc một số đối tượng không lập bản đồ khác, mã C có thể, một lần nữa, hoạt động không đúng. Một lần nữa, làm cho mã thực hiện kiểm tra loại không cần thiết sẽ là một sự lãng phí chu kỳ CPU.

Một khả năng khác là các hàm (thường được khai báo) borrow a reference vào không gian tên chung của mô-đun và làm cho thuộc tính có thể ghi sẽ làm cho số tham chiếu bị sai lệch.Tôi có thể tưởng tượng thiết kế này, nhưng tôi không thực sự chắc chắn đó là một ý tưởng tuyệt vời vì các hàm có thể được xây dựng một cách rõ ràng với các đối tượng có thời gian tồn tại ngắn hơn so với mô-đun sở hữu, và chúng cần phải được đặt biệt.

+0

Tôi không mua lời giải thích '__closure__': thêm một setter thực hiện kiểm tra kiểu nào đó sẽ dễ dàng (xem, ex, setter cho' __code__', thực hiện chính xác điều đó: https://github.com/python/ cpython/blob/master/Objects/funcobject.C# L254) –

+0

@DavidWolever: Nó vẫn là một trong những giao diện thân thiện với người dùng * ít nhất. Bạn phải nhận được thứ tự biến đúng, và nó không phải là một tuple của các đối tượng bình thường nhưng một tuple của "tế bào". Có lẽ loại mã kiểm tra đơn giản là rắc rối nhiều hơn giá trị của nó, trong tâm trí của các nhà phát triển cốt lõi. – Kevin

+0

Tôi chắc chắn không mua đối số '__globals__', xem xét' g = {k: v cho k, v trong func .__ globals __. Items() nếu k trong phép}; new_func = types.FunctionType (func .__ code__, g, func .__ name__, func .__ defaults__, func .__ closure __) 'là python hợp lệ – NightShadeQueen

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