2012-02-22 41 views
42

Trong mã sau đây, tại sao Python không biên dịch f2 sang cùng bytecode là f1?Tại sao Python không đánh giá số học liên tục trước khi biên dịch sang bytecode?

Có lý do nào không?

>>> def f1(x): 
    x*100 

>>> dis.dis(f1) 
    2   0 LOAD_FAST    0 (x) 
       3 LOAD_CONST    1 (100) 
       6 BINARY_MULTIPLY 
       7 POP_TOP 
       8 LOAD_CONST    0 (None) 
      11 RETURN_VALUE 
>>> def f2(x): 
     x*10*10 

>>> dis.dis(f2) 
    2   0 LOAD_FAST    0 (x) 
       3 LOAD_CONST    1 (10) 
       6 BINARY_MULTIPLY 
       7 LOAD_CONST    1 (10) 
      10 BINARY_MULTIPLY 
      11 POP_TOP 
      12 LOAD_CONST    0 (None) 
      15 RETURN_VALUE 

Trả lời

70

Điều này là do x có thể có phương pháp __mul__ có tác dụng phụ. x * 10 * 10 gọi __mul__ hai lần, trong khi x * 100 chỉ gọi nó một lần:

>>> class Foo(object): 
...  def __init__ (self): 
...    self.val = 5 
...  def __mul__ (self, other): 
...    print "Called __mul__: %s" % (other) 
...    self.val = self.val * other 
...    return self 
... 
>>> a = Foo() 
>>> a * 10 * 10 
Called __mul__: 10 
Called __mul__: 10 
<__main__.Foo object at 0x1017c4990> 

Tự động gấp các hằng số và chỉ gọi __mul__ một lần có thể thay đổi hành vi.

Bạn có thể nhận được tối ưu hóa mà bạn muốn bằng cách sắp xếp lại thao tác sao cho các hằng số được nhân trước (hoặc, như đã đề cập trong các chú thích, sử dụng dấu ngoặc đơn để nhóm chúng sao cho chúng chỉ hoạt động cùng nhau, bất kể vị trí), do đó, hãy nêu rõ mong muốn của bạn về việc gấp để xảy ra:

>>> def f1(x): 
...  return 10 * 10 * x 
... 
>>> dis.dis(f1) 
    2   0 LOAD_CONST    2 (100) 
       3 LOAD_FAST    0 (x) 
       6 BINARY_MULTIPLY  
       7 RETURN_VALUE 
+0

Lưu ý rằng thậm chí không có gì được chuyển cho phương thức, kết quả của 'dis' vẫn giống nhau, nếu nội dung của phương thức là:' x = 9; y = x * 10 * 10; ', kết quả vẫn là cùng viz. tải const hai lần. Có vẻ như Python không thực hiện tối ưu hóa toàn bộ phương thức? –

+5

@ SanjayT.Sharma: Trình biên dịch vẫn không thể thực sự biết 'x' là gì, vì vậy nó phải chơi nó an toàn. Khả năng sửa đổi thời gian chạy linh hoạt và khả năng sửa đổi thời gian chạy động của Python làm cho nó có thể thay đổi kiểu 'x' bên trong các hàm địa phương. –

+6

'x * (10 * 10)' cũng hoạt động và rõ ràng hơn một chút. – WolframH

17

Python đánh giá biểu thức từ left to right. Đối với f2(), điều này có nghĩa đầu tiên nó sẽ đánh giá x*10 và sau đó nhân kết quả bằng 10. Hãy thử:

Hãy thử:

def f2(x): 
    10*10*x 

này nên được tối ưu hóa.

+0

Điều này làm việc cho tôi trong cpython 2.7.2. – jcollado

+1

Tuyệt! cũng xác nhận trên cpython 2.6.6 – Jonathan

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