2012-11-09 2 views
8

ich einen Code haben, der eine Menge von String-Formatierung tut, oft habe ich am Ende mit Code bis entlang der Linien von:Ist ein Zeichenfolgenformatierer, der Variablen aus seinem aufrufenden Bereich abzieht, eine schlechte Praxis?

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

Wo ich versuche, eine große Anzahl von Variablen in eine große Zeichenfolge zu interpolieren .

Gibt es einen guten Grund, keine Funktion wie diese zu schreiben, die das Modul inspect verwendet, um zu interpolierende Variablen zu finden?

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

könnten Sie auch 'Einheimischen nur()' oder 'Globals()' –

+0

@FC: In der Tat, aber 'interpolieren mit ("...", ** Einheimische()) 'über den ganzen Ort sieht chaotisch, und es scheitert in seltenen Fällen, wo' s 'ist eine lokale Variable, da es versucht, das erste Argument zweimal setzen – Eric

+0

Ich werde die offensichtliche Frage stellen. Warum sind diese Variablen im globalen Bereich und nicht in einem Wörterbuch an erster Stelle? – Wilduck

Antwort

8

Ein einfacher und sicherer Ansatz wäre der folgende Code. inspect.currentframe ist nicht bei allen Implementierungen von Python verfügbar, sodass der Code unterbrochen wird, wenn dies nicht der Fall ist. Unter jython, ironpython oder pypy ist es vielleicht nicht verfügbar, weil es eine Cpython-Sache zu sein scheint. Dies macht Ihren Code weniger portabel.

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

diese Technik tatsächlich in dem auch, indem man die Tabelle als Keyword-Argumente mit der ‚**‘ Notation erfolgt Dies könnte

Python tutorial's Input and Output chapter beschrieben. Dies ist besonders nützlich in Kombination mit der neuen integrierten vars() - Funktion, die ein Wörterbuch zurückgibt, das alle lokalen Variablen enthält.

auch in der Python-Dokumentation für inspect.currentframe

CPython Implementierung Detail: Diese Funktion setzt auf Python-Stack Frame-Unterstützung in den Dolmetschern, die nicht garantiert ist in alle Implementierungen von Python existieren. Wenn in einer Implementierung ohne Python-Stack-Frame-Unterstützung ausgeführt wird, gibt diese Funktion None zurück.

2

Im inspect Modul wird currentframe wie folgt definiert:

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

Also, wenn sys ein _getframe Attribut hat, wird nicht funktionieren, die interpolate Funktion.

The docs für sys._getframe sagen:

CPython Implementierung Detail: Diese Funktion sollte nur für internen und speziellen Zweck verwendet werden. Es ist nicht garantiert, dass in allen Implementierungen von Python existiert.


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

im Funktionskörper ist, dass nicht viel länger als

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

und Code schreiben mehr tragbar.

3

Die gute alte Postbote hat eine Funktion _, die genau diese Sache tut:

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 

Also, wenn Barry Warschau das tun können, warum können wir nicht?

4

Update: Python 3.6 hat diese Funktion (eine leistungsstärkere Variante) builtin:

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

Siehe PEP 0498 -- Literal String Interpolation


Es [manuelle Lösung] mit verschachtelten Funktionen zu etwas überraschenden Verhalten führt :

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() 

Hinweis: Derselbe Aufruf ist erfolgreich oder schlägt fehl, je nachdem, ob eine Variable über oder unter dieser Variable erwähnt wird.

Wo callerscope ist:

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)) 
Verwandte Themen