2012-11-09 14 views
8

Tôi có một số mã mà không một awful nhiều định dạng chuỗi, Thông thường, tôi kết thúc với mã dọc theo dòng:Trình định dạng chuỗi có kéo biến từ thực tiễn xấu phạm vi gọi điện của nó không?

"...".format(x=x, y=y, z=z, foo=foo, ...) 

Nơi tôi đang cố gắng để suy một số lượng lớn các biến thành một chuỗi lớn .

Có lý do chính đáng để không viết chức năng như thế này sử dụng mô-đun inspect để tìm các biến nội suy không?

import inspect 

def interpolate(s): 
    return s.format(**inspect.currentframe().f_back.f_locals) 

def generateTheString(x): 
    y = foo(x) 
    z = x + y 
    # more calculations go here 
    return interpolate("{x}, {y}, {z}") 
+1

bạn cũng có thể chỉ cần sử dụng 'người dân địa phương() 'hoặc' globals() ' –

+0

@FC: Trên thực tế, nhưng có' Nội suy ("...", người dân địa phương **()) 'tất cả các nơi trông lộn xộn, cộng với nó thất bại trong trường hợp hiếm hoi mà' s' là một biến địa phương, vì nó cố gắng thiết lập đối số đầu tiên hai lần – Eric

+0

Tôi sẽ đặt câu hỏi rõ ràng. Tại sao các biến này trong phạm vi toàn cầu và không có trong từ điển ở vị trí đầu tiên? – Wilduck

Trả lời

8

Cách tiếp cận đơn giản và an toàn hơn là mã bên dưới. inspect.currentframe không có sẵn trên tất cả việc triển khai python để mã của bạn có thể bị phá vỡ khi không. Theo jython, ironpython, hoặc pypy nó có thể không có sẵn bởi vì nó có vẻ là một điều cpython. Điều này làm cho mã của bạn kém di động hơn.

print "{x}, {y}".format(**vars()) 

kỹ thuật này là thực sự được mô tả trong Python tutorial's Input and Output chapter

này cũng có thể được thực hiện bằng cách thông qua bảng các đối số như từ khóa với các ‘**’ ký hiệu. Điều này đặc biệt hữu ích khi kết hợp với hàm vars() tích hợp mới , trả về một từ điển chứa tất cả các biến cục bộ.

cũng trong tài liệu python cho inspect.currentframe

CPython chi tiết thực hiện: Chức năng này dựa trên Python chồng hỗ trợ khung trong phiên dịch, mà không được bảo đảm để tồn tại trong mọi cài đặt của Python. Nếu chạy trong triển khai mà không cần hỗ trợ khung ngăn xếp Python, hàm này sẽ trả về Không.

2

Trong mô-đun inspect, currentframe được định nghĩa như thế này:

if hasattr(sys, '_getframe'): 
    currentframe = sys._getframe 
else: 
    currentframe = lambda _=None: None 

Vì vậy, trừ sys có một thuộc tính _getframe, các interpolate chức năng sẽ không hoạt động.

The docs cho sys._getframe nói:

CPython chi tiết thực hiện: Chức năng này nên được sử dụng cho chỉ mục đích nội bộ và chuyên ngành. Nó không được bảo đảm để tồn tại trong tất cả các triển khai Python.


Viết

"{x}, {y}, {z}".format(**vars()) 

trong cơ thể chức năng không phải là dài hơn nhiều so với

interpolate("{x}, {y}, {z}") 

và mã của bạn sẽ được cầm tay hơn.

3

Các đưa thư cũ tốt có chức năng _ thực hiện chính xác điều này:

def _(s): 
    if s == '': 
     return s 
    assert s 
    # Do translation of the given string into the current language, and do 
    # Ping-string interpolation into the resulting string. 
    # 
    # This lets you write something like: 
    # 
    #  now = time.ctime(time.time()) 
    #  print _('The current time is: %(now)s') 
    # 
    # and have it Just Work. Note that the lookup order for keys in the 
    # original string is 1) locals dictionary, 2) globals dictionary. 
    # 
    # First, get the frame of the caller 
    frame = sys._getframe(1) 
    # A `safe' dictionary is used so we won't get an exception if there's a 
    # missing key in the dictionary. 
    dict = SafeDict(frame.f_globals.copy()) 
    dict.update(frame.f_locals) 
    # Translate the string, then interpolate into it. 
    return _translation.gettext(s) % dict 

Vì vậy, nếu Barry Warsaw có thể làm điều đó, tại sao không thể chúng ta?

4

Cập nhật: Python 3.6 có tính năng này (một biến thể mạnh hơn) được xây dựng trong:

x, y, z = range(3) 
print(f"{x} {y + z}") 
# -> 0 3 

Xem PEP 0498 -- Literal String Interpolation


Nó [giải pháp thủ công] dẫn hành vi để điều khá ngạc nhiên với chức năng lồng nhau :

from callerscope import format 

def outer(): 
    def inner(): 
     nonlocal a 
     try: 
      print(format("{a} {b}")) 
     except KeyError as e: 
      assert e.args[0] == 'b' 
     else: 
      assert 0 

    def inner_read_b(): 
     nonlocal a 
     print(b) # read `b` from outer() 
     try: 
      print(format("{a} {b}")) 
     except KeyError as e: 
      assert 0 
    a, b = "ab" 
    inner() 
    inner_read_b() 

Lưu ý: cùng một cuộc gọi thành công hoặc thất bại tùy thuộc vào việc một biến được đề cập ở đâu đó ở trên hay dưới nó.

đâu callerscope là:

import inspect 
from collections import ChainMap 
from string import Formatter 

def format(format_string, *args, _format=Formatter().vformat, **kwargs): 
    caller_locals = inspect.currentframe().f_back.f_locals 
    return _format(format_string, args, ChainMap(kwargs, caller_locals)) 
Các vấn đề liên quan