2009-07-17 36 views
5

Làm cách nào để sửa đổi không gian tên cục bộ của hàm trong python? Tôi biết rằng local() trả về không gian tên cục bộ của hàm khi được gọi bên trong nó, nhưng tôi muốn làm một cái gì đó như thế này (tôi có một lý do tại sao tôi muốn làm điều này trong đó g không thể truy cập được f, nhưng nó nhanh hơn để cung cấp cho một ví dụ nhỏ nhặt, ngu xuẩn để minh họa cho vấn đề):Cách sửa đổi không gian tên cục bộ trong python

def g(): 
    pass 

def f(): 
    g() 

f.add_to_locals({'g':g}) 
+0

mục tiêu cuối cùng của bạn sử dụng kỹ thuật kỳ lạ này là gì? Tại sao bạn không thể xác định g() trước f()? –

+0

Tôi đã cố gắng để gây rối với không gian tên của một chức năng ở thời gian chạy trước đó (để thử nghiệm/mocking) và thất bại ... chỉ FYI. –

+0

heck là 'add_to_locals' là gì? Nó dường như không tồn tại đối với tôi. Đối tượng 'AttributeError: 'function' không có thuộc tính 'add_to_locals'' –

Trả lời

3

Vì chức năng không được gọi, nó chưa có khung ngăn xếp "cục bộ". Giải pháp đơn giản nhất là sử dụng một bối cảnh toàn cầu:

handler = None 
def f(): 
    handler() 

def g(): pass 

handler = g 

Hoặc bạn có thể thiết lập g trên đối tượng chức năng:

f.g = g 

Nhưng tôi không chắc chắn làm thế nào bạn có thể nhận được các đối tượng chức năng từ bên trong chính hàm đó. Nếu đó là một phương pháp, bạn sẽ sử dụng self.

+0

Nếu bạn thực sự * * muốn sử dụng đối tượng chức năng bên trong hàm, bạn có thể sử dụng kiểm tra: nhập khẩu kiểm tra nhập khẩu pprint def fn(): khung = inspect.currentframe() frame.f_globals in [khung .f_code.co_name] .g fn.g = 'hello' fn() – Ivo

+0

Đã bị hỏng đối với các hàm lớp, chức năng lồng/đóng và lambdas và mọi thứ được trang trí - nó sẽ trả về chức năng gói của người trang trí, chứ không phải chức năng thực tế. –

+0

nếu 'f' là phương thức lớp/thể hiện thì sao? –

1

Một chức năng không thực thi không có bất kỳ người dân địa phương nào; bối cảnh cục bộ được tạo ra khi bạn chạy hàm, và bị phá hủy khi nó thoát, do đó không có "không gian tên cục bộ" để sửa đổi từ bên ngoài hàm.

Bạn có thể làm một cái gì đó như thế này, mặc dù:

def f(): 
    g = [1] 
    def func(): 
     print g[0] 
    return func, g 

f, val = f() 
f() 
val[0] = 2 
f() 

này sử dụng một mảng để mô phỏng một tài liệu tham khảo.

+2

Trang web này thực sự cần giải thích cho -1 phiếu bầu, vì vậy mọi người không thể bỏ phiếu ẩn danh các câu trả lời đúng mà không có lý do. –

2

Tôi nghĩ bạn có thể giải quyết vấn đề giải quyết vấn đề từ một điểm hoàn toàn khác.
Chức năng là đối tượng, với từ điển của chúng; do đó, bạn có thể thêm g đến f, và sử dụng nó:

def g(): 
    print "g" 

def f(): 
    f.g() 

f.g = g 
+0

Giải pháp rất thanh lịch. +1 – Josip

+0

Tính năng này chỉ hoạt động cho các chức năng toàn cầu; không có cách nào để truy cập f đáng tin cậy từ bên trong f. –

+0

Bạn có thể mở rộng điều này không? Các hàm Python là các đối tượng và không có sự khác biệt nếu là cục bộ hoặc toàn cầu - trừ khi bạn có nghĩa là _methods_ nhưng đó là một câu chuyện hoàn toàn khác, và không phải là những gì OP yêu cầu. –

3

Tại sao bạn không chỉ cần thêm một cuộc tranh cãi để f() và vượt qua một tham chiếu đến g()?

def g(): 
    pass 

def f(func): 
    func() 

f(g) 
2

Tôi giả sử bạn muốn thực hiện điều này, vì hàm f được xác định không phải do bạn, mà bởi một số mô-đun khác. Vì vậy, bạn muốn thay đổi cách f() hoạt động. Đặc biệt, bạn muốn thay đổi cái được gọi khi g được gọi.

Vì vậy, tôi sẽ đề nghị này:

import thirdpartypackage 

def mynewg(): 
    pass 

thirdpartypackage.g = mynewg 

này sẽ thay đổi g toàn cầu cho thirdpartypackage module. Vì vậy, khi thirdpartypackage.f() bây giờ được gọi, nó sẽ gọi mynewg() thay vì g().

Nếu điều này không giải quyết được, có thể thực tế là g() được nhập từ withing f() hoặc somthing. Sau đó, giải pháp là:

import thirdpartypackage 

def mynewg(): 
    pass 

deg mynewf(): 
    mynewg() 

thirdpartypackage.f = mynewf 

Tức là, bạn ghi đè f() hoàn toàn bằng phiên bản đã sửa đổi thực hiện những gì bạn muốn.

8

Bạn có một vài tùy chọn. Đầu tiên, lưu ý rằng g trong ví dụ của bạn không thực sự là một hàm cục bộ (ví dụ, không được gán bên trong nó), nó là một global (nghĩa là chưa được gán cho một biến cục bộ). Điều này có nghĩa là nó sẽ được tra cứu trong module mà hàm được định nghĩa. Điều này thật may mắn vì không có cách nào thay đổi người dân bên ngoài (thiếu vá mã byte), khi chúng được gán khi hàm chạy, không phải trước đó.

Một tùy chọn đơn giản là đưa hàm của bạn vào không gian tên của mô-đun. Điều này sẽ làm việc, nhưng sẽ ảnh hưởng đến mọi chức năng trong mô-đun đó truy cập biến, thay vì chỉ là một hàm.

Để chỉ ảnh hưởng đến một chức năng, bạn cần thay vào đó chỉ ra rằng func_globals ở một nơi khác. Thật không may, đây là một thuộc tính chỉ đọc, nhưng bạn có thể làm những gì bạn muốn bằng cách tái tạo lại chức năng với cơ thể giống nhau, nhưng một namespace toàn cục khác nhau:

import new 
f = new.function(f.func_code, {'g': my_g_function}, f.func_name, f.func_defaults, f.func_closure) 

f bây giờ sẽ có indentical, ngoại trừ việc nó sẽ xem xét cho globals trong dict được cung cấp. Lưu ý rằng điều này sẽ khôi phục toàn bộ không gian tên chung - nếu có các biến có f thì tra cứu, hãy đảm bảo bạn cũng cung cấp các biến đó. Điều này cũng khá khó khăn mặc dù, và có thể không hoạt động trên các phiên bản của python khác với cpython.

+1

Tôi không biết nếu điều này là "tốt" mã, nhưng nó đã được chính xác những gì tôi muốn. Tôi đang chuyển đổi một codebase sang một codebase khác và tôi muốn có thể mô phỏng một ngôn ngữ xây dựng từ ngôn ngữ khác. Ngôn ngữ khác có thể đóng gói mã và thêm hoặc xóa mã đó khỏi không gian tên. Nó rất hữu ích trong việc ghi đè các hàm khác nhau. Vì vậy, nó thao túng không gian tên trong một chồng như thời trang. Đây là điều gần nhất mà tôi có thể tìm thấy cho phép tôi làm điều đó một cách hiệu quả. Cảm ơn – Demolishun

+0

thư viện 'mới' này là gì? –

+0

có gì sai với 'exec' hoặc' locals(). Update (d) '? –

0

Điều này dường như làm việc

def add_to_locals(l): 
    l['newlocal'] = 1 

add_to_locals(locals()) 
assert newlocal 
+0

Điều này không hoạt động bên trong một chức năng. Nó không thêm giá trị vào không gian tên. – Demolishun

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