web-dev-qa-db-fra.com

python format de chaîne () avec dict avec clés entières

Je voudrais utiliser Python chaîne format() pour agir comme un modèle rapide et sale. Cependant, le dict que je voudrais utiliser a des clés qui sont des (représentations de chaîne) d'entiers. un exemple simplifié suit:

s = 'hello there {5}'
d = {'5': 'you'}
s.format(**d)

le code ci-dessus renvoie l'erreur suivante:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: Tuple index out of range

est-il possible de faire ce qui précède?

17
yee379

Nous avons établi que cela ne fonctionnera pas, mais que diriez-vous d'une solution:

Bien que str.format ne fonctionnera pas dans ce cas, assez drôle l'ancien % formatage. Ce n'est pas recommandé, mais vous avez demandé un modèle rapide et sale.

>>> 'hello there %(5)s' % {'5': 'you'}
'hello there you'

Notez cependant que cela ne fonctionnera pas pour les clés entières.

>>> 'hello there %(5)s' % {5: 'you'}

Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    'hello there %(5)s' % {5: 'you'}
KeyError: '5'
28
Volatility

J'adore l'idée d'étendre le formateur afin qu'il autorise des noms de champs arbitraires (entiers, noms de champs avec deux points, etc.). Une implémentation pourrait ressembler à ceci:

import string, re

class QuFormatter(string.Formatter):
    def _quote(self, m):
        if not hasattr(self, 'quoted'):
            self.quoted = {}
        key = '__q__' + str(len(self.quoted))
        self.quoted[key] = m.group(2)
        return '{' + m.group(1) + key + m.group(3) + '}'

    def parse(self, format_string):
        return string.Formatter.parse(self,
            re.sub(r'{([^}`]*)`([^}`]*)`([^}]*)}', self._quote, format_string))

    def get_value(self, key, args, kwargs):
        if key.startswith('__q__'):
            key = self.quoted[key]
        return string.Formatter.get_value(self, key, args, kwargs)

Usage:

d = {'5': 'you', '6': 'me', "okay":1, "weird:thing!": 123456}
print QuFormatter().format(
     'hello there {`5`} {`6`:20s}--{okay}--{`weird:thing!`:20,d}', 
     **d)

Les champs en guillemets sont donc traités littéralement.

8
georg

Voir cet article pour les réponses à vos problèmes. Il semble que vous ne puissiez pas utiliser des chaînes composées de nombres comme clés de dictionnaire dans les chaînes de format ( lien docs ).

Si vous pouvez utiliser une clé autre que 5, cela fonctionnera:

my_string='hello there {spam:s}'
d={'spam': 'you'}
print my_string.format(**d) # Returns "hello there you"
7
Ffisegydd

De PEP 3101

La classe de chaîne intégrée (et également la classe unicode en 2.6) gagnera une nouvelle méthode, 'format', qui prend un nombre arbitraire d'arguments positionnels et de mots clés:

"The story of {0}, {1}, and {c}".format(a, b, c=d)

Dans une chaîne de format, chaque argument positionnel est identifié par un nombre, à partir de zéro, donc dans l'exemple ci-dessus, 'a' est l'argument 0 et 'b' est l'argument 1. Chaque argument de mot-clé est identifié par son nom de mot-clé, donc dans l'exemple ci-dessus, "c" est utilisé pour faire référence au troisième argument.

Valeurs numériques utilisées dans str.format sont des arguments positionnels. Vous ne pouvez donc pas faire cela.

Vous pouvez joindre le PEP 3101 depuis ici . La section connexe est sous Méthodes de chaîne

Comme @Volatility l'a mentionné, vous pouvez utiliser % formateur pour cela.

3
FallenAngel

Vous pouvez faire quelque chose avec get_value Dans un string.Formatter Personnalisé pour essayer les champs de remplacement comme clés de dictionnaire avant de retomber sur l'index en clés arg - notez le conflit possible de priorité et d'intention ici ... donc c'est pas exactement recommandé, mais une idée de ce qui est possible:

import string

class MyFormatter(string.Formatter):
    def get_value(self, key, args, kwargs):
        try:
            return kwargs[str(key)]
        except KeyError:
            return super(MyFormatter, self).get_value(key, args, kwargs)

s = 'hello there {5} - you are number {0}'
d = {'5': 'you'}
print MyFormatter().format(s, 1, 2, 3, **d)
# hello there you - you are number 1
1
Jon Clements

C'est effectivement possible, en utilisant le fait {k} cherche le (k+1)th argument positionnel.

def populate_list(d):
   """ Return a list l verifying l[k] = d[str(k)] for each natural k """
   return [d.get(str(k)) for k in range(1 + max(map(int, d)))] if d else []

def format_with_int_keys(s,d):
   """ Replace each {k} in s by d[str(k)] """
   return s.format(*populate_list(d))

s = 'hello there {5}'
d = {'5': 'you'}
print (format_with_int_keys(s,d))

Edit: Il s'agit en fait d'une version détaillée de la solution @wim.

0
YvesgereY