2010-09-19 19 views
6

Tôi đang viết một số mã để xác định tên mà đối tượng được gán. Điều này là dành cho công việc gỡ lỗi chung và để làm quen với python internals.Truy cập tên mà đối tượng đang được tạo được gán cho

Tôi có nó có cấu trúc như là một trang trí lớp để tất cả các trường hợp của lớp đó sẽ có tên của họ được ghi lại nếu nó có thể làm. Mã này khá dài nên tôi sẽ không đăng nó trừ khi được hỏi. Kỹ thuật nói chung là như sau mặc dù

  1. trang trí lớp __init__ phương pháp với mã để làm những gì tôi muốn

  2. thiết caller = inspect.currentframe().f_back và mở inspect.getframeinfo(caller).filename và gửi cho ast.parse. Tôi không làm bất kỳ lỗi kiểm tra ở đây vì (1) đây chỉ là để gỡ lỗi/profiling/hack (2) quá trình này chính xác đã được 'chỉ' hoàn thành hoặc mã sẽ không được chạy. Có vấn đề gì với điều này không?

  3. tìm ra dụ ast.Assignment gây ra hiện đang thực hiện __init__ phương pháp để chạy

  4. nếu len(assignment.targets) == 1 sau đó chỉ có một mục ở phía bên tay trái, và tôi có thể có được tên ra khỏi targets[0].id. Trong một biểu mẫu đơn giản như a = Foo(), thì assignment.value là một phiên bản của ast.Call. nếu đó là một chữ (ví dụ: danh sách), thì value sẽ là danh sách đó và được bảo lãnh bởi vì đối tượng tôi quan tâm không được gán cho tên.

cách tốt nhất là gì để xác nhận rằng assignment.value.func là trong thực tế type(obj).__call__ của đối tượng mà tôi quan tâm. Tôi khá chắc chắn rằng tôi đảm bảo rằng Đó là "ở đâu đó" hoặc mã thậm chí sẽ không chạy. Tôi chỉ cần cho nó để được ở cấp cao nhất. Điều hiển nhiên cần làm là đi bộ và đảm bảo rằng nó không chứa bất kỳ cuộc gọi nội bộ nào. Sau đó, tôi đảm bảo rằng tôi có tên. (Lý luận của tôi là chính xác, tôi không chắc chắn nếu giả định của nó là). Đây không phải là lý tưởng mặc dù bởi vì nếu tôi quan tâm đến Foo, điều này có thể dẫn tôi quăng đi a = Foo(Bar()) bởi vì tôi không biết nếu đó là a = Bar(Foo()).

Tất nhiên tôi chỉ có thể kiểm tra assignment.value.func.id nhưng sau đó ai đó có thể làm Foobar = Foo hay cái gì vì vậy tôi không muốn dựa vào điều này quá nặng nề

giúp đỡ Bất kỳ sẽ được đánh giá rất nhiều. Như thường lệ, tôi quan tâm đến bất kỳ đề xuất hoặc vấn đề nào khác mà tôi có thể nhìn thấy.

Ngoài ra, tôi thực sự ngạc nhiên khi tôi vừa phát minh ra thẻ 'python-internals'.

+0

+ nhiều - Python rất tuyệt! – katrielalex

Trả lời

2

AST không thể cung cấp cho bạn câu trả lời đó. Hãy thử sử dụng frame.f_lasti và sau đó nhìn trộm vào bytecode. Nếu dòng tiếp theo không phải là STORE_FAST, bạn đã có cuộc gọi nội bộ hoặc một số nội dung khác đang diễn ra khác với nhiệm vụ đơn giản mà bạn đang tìm kiếm.

def f(): 
    f = sys._getframe() 
    i = f.f_lasti + 3 # capture current point of execution, advance to expected store 
    print dis.disco(f.f_code, i) 
0

Tôi không biết mức độ trợ giúp này là bao nhiêu, nhưng bạn có cân nhắc sử dụng cuộc gọi đến locals() không? Nó trả về một dict có chứa tên và giá trị của tất cả các biến cục bộ.

Ví dụ:

s = '' 
locals() 
>>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', '__name__': '__main__', '__doc__': None} 
t = s # I think this is what is of most importance to you 
locals() 
>>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', 't': '', '__name__': '__main__', '__doc__': None} 

Vì vậy, bạn có thể đi qua từ điển này và kiểm tra mà các biến có (như giá trị của họ) một đối tượng của loại mà bạn đang tìm kiếm. Như tôi đã nói, tôi không biết câu trả lời này giúp ích bao nhiêu, nhưng nếu bạn cần làm rõ về bất cứ điều gì, thì hãy để lại nhận xét và tôi sẽ cố gắng trả lời hết mức có thể.

+0

Điều này không có tác dụng vì 'locals()' luôn đề cập đến khung mà nó được gọi, tôi đang tìm kiếm một khung hình. Tôi có thể nhận được điều đó với 'sys._getframe()' cho 'inspect.currentfrmae'. Vấn đề là 'foo = bar()' không tạo một mục nhập trong 'locals()' (tham chiếu đến khung mà phép gán diễn ra trong) cho đến khi _after_ 'bar .__ init __()' trả về. Nhưng đó là nơi thích hợp để có được tên bởi vì tôi chỉ có thể làm điều đó với một trang trí trên 'bar' thay vì đặt mã sau khi chuyển nhượng _every_. – aaronasterling

+0

@AaronMcSmooth: Có cuộc gọi đến globals() thay vì người dân địa phương khắc phục sự cố này – inspectorG4dget

+1

@ InspectoG4det Không vì lý do tương tự. 'globals' chỉ là' frame.f_locals' cho khung ngoài cùng và vì vậy một mục không phải là các vị trí trong 'frame.f_locals' cho đến khi khung bên trong (' bar .__ init __() 'trong trường hợp này) trả về. – aaronasterling

0

Tôi không kiểm tra lỗi ở đây vì (1) đây chỉ là để gỡ lỗi/lược tả/hack (2) quá trình này chính xác là 'vừa hoàn thành' hoặc mã sẽ không chạy. Có vấn đề gì với điều này không?

Có:

  1. Bắt đầu một chương trình

  2. đơn vị Chờ nó nhập khẩu một foo.py module cụ thể

  3. Sửa foo.py

Bây giờ mã được tải bằng Python quá trình không khớp với mã được tìm thấy trên đĩa.

Một lý do nữa giải thích tại sao việc tháo bytecode có thể là một kỹ thuật tốt hơn.

0

Đây là cách thực hiện. Phần lớn nhờ vào người dẫn đầu ẩn danh. Rất nhiều may mắn trong nhiệm vụ của bạn để rack lên đại diện cho tài khoản alt của bạn.

import inspect 
import opcode 


def get_name(f): 
    """Gets the name that the return value of a function is 
    assigned to. 

    This could be modified for classes as well. This is a 
    basic version for illustration that only prints out 
    the assignment instead of trying to do anything with it. 
    A more flexible way would be to pass it a callback for when 
    it identified an assignment. 

    It does nothing for assignment to attributes. The solution 
    for that isn't much more complicated though. If the 
    instruction after the function call is a a `LOAD_GLOBAL`, 
    `LOAD_FAST` or `LOAD_DEREF`, then it should be followed by 
    a chain of `LOAD_ATTR`'s. The last one is the attribute 
    assigned to. 
    """ 

    def inner(*args, **kwargs): 
     name = None 

     frame = inspect.currentframe().f_back 
     i = frame.f_lasti + 3 

     # get the name if it exists 
     code = frame.f_code 
     instr = ord(code.co_code[i]) 
     arg = ord(code.co_code[i+1]) # no extended arg here. 
     if instr == opcode.opmap['STORE_FAST']: 
      name = code.co_varnames[arg] 
     elif instr in (opcode.opmap['STORE_GLOBAL'], 
         opcode.opmap['STORE_NAME']): 
      name = code.co_names[arg] 
     elif instr == opcode.opmap['STORE_DEREF']: 
      try: 
       name = code.co_cellvars[arg] 
      except IndexError: 
       name = code.co_freevars[arg - len(code.co_cellvars)] 
     ret = f(*args, **kwargs) 
     print opcode.opname[instr] 
     if name: 
      print "{0} = {1}".format(name, ret) 
     return ret 

    return inner 


@get_name 
def square(x): 
    return x**2 

def test_local(): 
    x = square(2) 

def test_deref(): 
    x = square(2) 
    def closure(): 
     y = x 
    return closure 

x = square(2) 
test_local() 
test_deref()() 

Nó không nên quá khó để tìm ra phân công của cho list_[i] = foo() một trong hai, bao gồm giá trị của i bằng cách sử dụng frame.f_locals. Những thứ khó hiểu sẽ là chữ và khi nó được thông qua như một đối số. Cả hai trường hợp này đều khá khó khăn.

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