Lasciando valori vuoti se non passato in str.format

voti
17

Ho incontrato un problema abbastanza semplice che non riesco a trovare una soluzione elegante per.

Sto creando una stringa utilizzando str.formatin una funzione che viene passata in una dictdi sostituzioni da utilizzare per il formato. Voglio creare la stringa e formattarla con i valori se sono passati e li lasciare in bianco il contrario.

Ex

kwargs = {name: mark}
My name is {name} and I'm really {adjective}..format(**kwargs)

dovrebbe restituire

My name is mark and I'm really .

invece di buttare un KeyError(Che è quello che accadrebbe se non facciamo nulla).

Imbarazzante, non riesco nemmeno a trovare una soluzione poco elegante per questo problema. Credo che avrei potuto risolvere questo problema semplicemente non si usa str.format, ma io preferisco usare il built-in (che in gran parte quello che voglio), se possibile.

Nota: Non so in anticipo quali saranno utilizzati chiavi. Sto cercando di non riuscire con grazia se qualcuno comprende una chiave, ma non metterlo nel dict kwargs. Se avessi saputo con precisione del 100% quello che le chiavi sarebbe alzò gli occhi, avevo appena popolano tutti loro e da fare con esso.

È pubblicato 05/11/2013 alle 19:43
dall'utente
In altre lingue...                            


7 risposte

voti
-1

Un modo per evitare l'errore chiave è quello di includere nel dict ma lasciarlo vuoto:

kwargs = {"name": "mark", "adjective": ""}
"My name is {name} and I'm really {adjective}.".format(**kwargs)

argomenti chiave si aspettano che il loro di essere una chiave in kwargs. Un altro modo per farlo sarebbe argomenti posizionali:

"My name is {0} and I'm really {1}.".format("mark")

Stampe "Mi chiamo Marco e sono davvero." Mentre

"My name is {0} and I'm really {1}.".format("mark","black")

Stampe "Mi chiamo Marco e sono davvero nero."

In alternativa, si può prendere il ValueError.

Risposto il 05/11/2013 a 19:51
fonte dall'utente

voti
10

Qui è un'opzione che utilizza collections.defaultdict:

>>> from collections import defaultdict
>>> kwargs = {"name": "mark"}
>>> template = "My name is {0[name]} and I'm really {0[adjective]}."
>>> template.format(defaultdict(str, kwargs))
"My name is mark and I'm really ."

Si noti che non stiamo usando **per decomprimere il dizionario in argomenti a parola chiave più, e l'identificatore di formato utilizza {0[name]}e {0[adjective]}, il che indica che dovremmo eseguire una ricerca fondamentale sul primo argomento di format()utilizzare "name"e "adjective", rispettivamente. Usandodefaultdict una chiave mancante si tradurrà in una stringa vuota invece di sollevare KeyError.

Risposto il 05/11/2013 a 19:52
fonte dall'utente

voti
17

È possibile seguire la raccomandazione nel PEP 3101 e sottoclasse Formatter:

import string

class BlankFormatter(string.Formatter):
    def __init__(self, default=''):
        self.default=default

    def get_value(self, key, args, kwds):
        if isinstance(key, str):
            return kwds.get(key, self.default)
        else:
            return string.Formatter.get_value(key, args, kwds)

kwargs = {"name": "mark", "adj": "mad"}     
fmt=BlankFormatter()
print fmt.format("My name is {name} and I'm really {adj}.", **kwargs)
# My name is mark and I'm really mad.
print fmt.format("My name is {name} and I'm really {adjective}.", **kwargs)
# My name is mark and I'm really .  

A partire da Python 3.2, è possibile utilizzare .format_map come alternativa:

class Default(dict):
    def __missing__(self, key):
        return '{'+key+'}'

kwargs = {"name": "mark"}

print("My name is {name} and I'm really {adjective}.".format_map(Default(kwargs)))

stampe:

My name is mark and I'm really {adjective}.
Risposto il 05/11/2013 a 20:47
fonte dall'utente

voti
0

Per il record:

s = "My name is {name} and I'm really {adjective}."
kwargs = dict((x[1], '') for x in s._formatter_parser())
# Now we have: `kwargs = {'name':'', 'adjective':''}`.
kwargs.update(name='mark')
print s.format(**kwargs)  # My name is mark and I'm really .
Risposto il 01/06/2016 a 18:41
fonte dall'utente

voti
0

Volevo aggiungere una soluzione abbastanza semplice per la sostituzione di eventuali valori predefiniti necessari.

import string

class SafeDict(dict):
    def __init__(self, missing='#', empty='', *args, **kwargs):
        super(SafeDict, self).__init__(*args, **kwargs)
        self.missing = missing
        self.empty = empty
    def __getitem__(self, item):
        return super(SafeDict, self).__getitem__(item) or self.empty
    def __missing__(self, key):
        return self.missing

values = SafeDict(a=None, c=1})
string.Formatter().vformat('{a} {c} {d}', (), values)
# ' 1 #'
Risposto il 19/07/2017 a 20:26
fonte dall'utente

voti
0

Mentre sottoclasse una Formatterè probabilmente la risposta "giusta", è anche possibile seguire forte vena chiedere-per-il perdono-non-permesso di Python catturando il KeyError. Il vantaggio di questo approccio è che è facilmente flessibile: In particolare, è facile avere valori di "default" che non sono statico (cioè, solo la costante eventualmente vuoto), ma può dipendere il nome della chiave, come qui :

def f(s, **kwargs):
    """Replaces missing keys with a pattern."""
    RET = "{{{}}}"
    try:
        return s.format(**kwargs)
    except KeyError as e:
        keyname = e.args[0]
        return f(s, **{ keyname: RET.format(keyname) }, **kwargs)

che funzionerà nel seguente modo:

In [1]: f("My name is {name} and I'm really {adjective}.", **{"name": "Mark"})
Out[1]: "My name is Mark and I'm really {adjective}."

Questo può essere facilmente specializzata a fare ciò che il PO voleva:

def f_blank(s, **kwargs):
    """Replaces missing keys with a blank."""
    try:
        return s.format(**kwargs)
    except KeyError as e:
        keyname = e.args[0]
        return f(s, **{ keyname: "" }, **kwargs)

Ho avuto un po 'più divertente con questa idea: https://gist.github.com/jlumbroso/57951c06a233c788e00d0fc309a93f91

# (not a real import! just saying importing the code from the Gist)
from gist.57951c06a233c788e00d0fc309a93f91 import _make_f

# Define replacement f"..." compatible with Python 2 and 3
_f = _make_f(globals=lambda: globals(), locals=lambda: locals())

# Use:
s = "Test"
var = 1
assert _f("{s} {var}") == "Test 1"

# Inside a non-global scope, you may have to provide locals
def test():
    l_s = "Test"
    l_var = 1
    assert _f("{l_s} {l_var} / {s} {var}") == "{l_s} {l_var} / Test 1"
    assert _f("{l_s} {l_var} / {s} {var}", **locals()) == "Test 1 / Test 1"
Risposto il 05/07/2019 a 04:43
fonte dall'utente

voti
0

Se siete ancora su Python 2, è possibile utilizzare defaultdictcon string.Formatterper raggiungere questo obiettivo:

>>> format_string = '{foo:<2s}{bar:<3s}'
>>> data = {'bar': 'baz'}
>>> string.Formatter().vformat(format_string, (), defaultdict(str, data))
'  baz'
Risposto il 02/08/2019 a 06:42
fonte dall'utente

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more