2010-03-25 21 views
15

Tôi có thể xác định một chức năng, khi được gọi, chèn người dân địa phương mới vào phạm vi của người gọi không? Tôi có cảm giác rằng việc chuyển số người địa phương của người gọi() vào chức năng có thể hoạt động, nhưng có cách nào để làm những gì tôi muốn mà không cần phải thực hiện việc này không?Tiêm các biến vào phạm vi của người gọi?

+4

Giả sử người gọi được mong đợi hiểu được rủi ro của callee khi đi đến thị trấn với người dân địa phương. – jogloran

+0

Âm thanh như một công thức cho mì spaghetti –

+11

Nó được gọi là "trả lại". –

Trả lời

8

Theo quy tắc của Python, bạn không thể thay đổi người gọi locals; trong các triển khai hiện tại, nếu bạn thử (ví dụ như với ma thuật đen Anurag gợi ý), bạn sẽ không nhận được ngoại lệ (mặc dù tôi muốn thêm kiểm tra lỗi đó vào một số phiên bản tương lai), nhưng về cơ bản sẽ không hoạt động nếu người gọi của bạn một hàm (không phải nếu người gọi của bạn là mã cấp cao nhất của mô-đun) - các biến cục bộ thực tế của người gọi sẽ không bị ảnh hưởng. Điều này giữ cho dù người gọi locals của bạn được chuyển vào một cách rõ ràng hoặc được tìm nạp thông qua ma thuật đen: họ vẫn cần được coi là một dict chỉ đọc nếu mã của bạn có bất kỳ sự tỉnh táo nào. Thay vào đó, bạn có thể có thẻ người gọi trong một dict rõ ràng, thực, bình thường (có thể được khởi tạo từ locals() nếu bạn muốn), và tất cả các thay đổi mã của bạn thực hiện trong dict đó vẫn sẽ ở đó để sử dụng của người gọi - - không phải là "tên gọi mới" trong phạm vi địa phương của người gọi, nhưng chức năng cũng giống nhau nếu người gọi cần sử dụng x['foo'] hoặc x.foo hoặc (như bạn muốn) chỉ tên thanh là foo.

BTW, sử dụng cú pháp thuộc tính truy cập chứ không phải là cú pháp lập chỉ mục dict, bạn có thể làm:

class Bunch(object): pass 

... 

# caller code 
b = Bunch() 
thefun(b) 
print b.foo 

... 

# called function 
def thefun(b): 
    b.foo = 23 

này cũng bao gồm, với một biến thể nhỏ bé, trường hợp trong đó thefun muốn làm việc với cú pháp lập chỉ mục dict (nói rằng nội dung của nó là b['foo'] = 23 thay vì b.foo = 23): trong trường hợp đó, người gọi chỉ cần sử dụng thefun(vars(b)) thay vì đồng bằng thefun(b), nhưng nó có thể tiếp tục làm việc với cú pháp truy cập b.foo sau đó.

+0

Được rồi, có vẻ như không thực sự có thể làm những gì tôi muốn. Cảm ơn ý tưởng truy cập thuộc tính; nếu tôi không thể có barenames (x), điều đẹp nhất tiếp theo có lẽ là thuộc tính access (context.x). – jogloran

+0

Điều này là không đúng, xem câu trả lời dưới đây bằng cách sử dụng mô-đun kiểm tra của python xây dựng để xem cách thực hiện của nó. Python là một ngôn ngữ động hoàn chỉnh, bạn có thể ghi đè lên trình thông dịch riêng của nó nếu bạn muốn. Tôi chỉ trích dẫn http://xkcd.com/353/ – Pykler

+0

Alex là chính xác. Câu trả lời của @ Pykler chỉ hoạt động trong một trường hợp cụ thể và không nói chung. – ferrouswheel

17

Kiểm tra inspect module, được sử dụng bởi minimock để giả lập phạm vi của người gọi.

Mã này phải làm những gì bạn muốn chính xác:

import inspect 
def mess_with_caller(): 
    stack = inspect.stack() 
    try: 
     locals_ = stack[1][0].f_locals 
    finally: 
     del stack 
    locals_['my_new_function'] = lambda : 'yaaaaaay' 


mess_with_caller() 
print my_new_function() 
>>> Output: 'yaaaaaay' 
+0

FYI: kiểm tra được xây dựng trong mô-đun python. – Pykler

+0

Kìa sức mạnh của python! – Pykler

+2

Ma thuật đen đáng sợ, nhưng dù sao cũng mát mẻ. – Cerin

0

Đây không phải là có thể mà không thay đổi API, như CPython giữ một tham chiếu đến các danh sách đối số trong suốt thời gian của cuộc gọi chức năng.

Một cách để thực hiện điều này là chuyển một đối tượng và xóa trạng thái bên trong của đối tượng khi sử dụng nó.

class Obj(object): 
    def __init__(self, state): 
     self.state = state 

def f(o): 
    # Do stuff with o.state 
    del o.state