2014-12-09 29 views
20

Hãy xem xét ví dụ sau:phạm vi chức năng eval trong python

i=7 
j=8 
k=10 
def test(): 
    i=1 
    j=2 
    k=3 
    return dict((name,eval(name)) for name in ['i','j','k']) 

Nó trả về:

>>> test() 
{'i': 7, 'k': 10, 'j': 8} 

Tại sao eval không đi vào xem xét các biến được định nghĩa bên trong hàm? Từ tài liệu, tùy chọn bạn có thể vượt qua một globals và một từ điển người dân địa phương. Cuối cùng, làm thế nào tôi có thể sửa đổi trường hợp nhỏ này để làm cho nó hoạt động?

+3

bạn có thể sửa đổi nó để làm việc bằng cách thêm 'global' trước khi khai báo biến bên trong một hàm nhưng đó là một ý tưởng tồi, mặt khác, sử dụng 'eval' thường là một ý tưởng tồi. – Rusty

+0

Điều gì đã nói - trừ khi bạn chắc chắn rằng bạn _have_ sử dụng eval, hãy tránh xa nó. – l4mpi

+0

@ l4mpi Tôi biết rằng eval là một ý tưởng tồi, nhưng tôi chỉ chơi với các phạm vi và tôi không hiểu hành vi này – Pierpaolo

Trả lời

13

Máy phát điện là implemented as function scopes:

Phạm vi được xác định tên trong một khối lớp được giới hạn trong lớp khối; nó không mở rộng đến các khối mã của các phương thức - điều này bao gồm các biểu thức máy phát điện vì chúng được triển khai bằng cách sử dụng phạm vi chức năng .

Vì vậy, trình tạo bên trong nhà xây dựng dict() có từ điển locals() riêng. Bây giờ chúng ta hãy nhìn vào Py_eval's source code, đặc biệt khi cả hai globals()locals() là None:

if (globals == Py_None) { 
     globals = PyEval_GetGlobals(); 
     if (locals == Py_None) 
      locals = PyEval_GetLocals(); 
    } 

Vì vậy, ví dụ bạn PyEval_GetLocals() sẽ trống tại thời điểm vòng lặp được thực hiện và globals() sẽ là từ điển toàn cầu. Lưu ý rằng i, jk định nghĩa bên trong hàm đang không ở trong phạm vi địa phương của máy phát điện, chứ không phải họ đang ở trong phạm vi của nó kèm theo:

>>> dict((name,eval(name, globals(), {})) for name in ['i', 'j', 'k']) 
{'i': 7, 'k': 10, 'j': 8} 
4

này xảy ra vì sự biểu hiện phátphạm vi khác nhau đến chức năng:

>>> def test(): 
    i, j, k = range(1, 4) 
    return dict((j, locals()) for _ in range(i)) 

>>> test() 
{2: {'.0': <listiterator object at 0x02F50A10>, 'j': 2, '_': 0}} 

Sử dụng j bên trong phạm vi liên kết với nó từ chức năng, vì đó là phạm vi bao quanh gần nhất, nhưng ik không bị ràng buộc cục bộ (vì k không được tham chiếu và i chỉ được sử dụng để tạo range).


Lưu ý rằng bạn có thể tránh vấn đề này với:

return dict(i=i, j=j, k=k) 

hoặc một chữ từ điển:

return {'i': i, 'j': j, 'k': k} 
Các vấn đề liên quan