2016-01-19 8 views
7

C'è un modo elegante per ottenere i nomi delle variabili di tipo %s -like dell'oggetto stringa? Ti piace questa:Come ottenere i nomi delle variabili nominate dalla stringa python

string = '%(a)s and %(b)s are friends.' 
names = get_names(string) # ['a', 'b'] 

noti modi alternativi:

  1. nomi Parse uso delle espressioni regolari, ad es .:

    import re 
    names = re.findall(r'%\((\w)\)[sdf]', string) # ['a', 'b'] 
    
  2. Usa .format() formattazione compatibile e Formatter().parse(string).

    How to get the variable names from the string for the format() method

Ma che dire di una stringa con le variabili% s-come?

PS: python 2.7

+2

Il metodo che stai descrivendo sembra funzionare bene. Restituisce ['a', 'b']. Quindi cosa manca adesso? –

+0

@AdiLevin Il modo n. 1 richiede un'importazione aggiuntiva. Il modo in cui n.2 richiede un altro formato di stringa. Sono solo curioso che esista un modo per ottenere lo stesso risultato usando solo metodi e proprietà interne dell'oggetto 'stringa' o, forse, alcune funzioni del modulo stringa. – hackprime

+0

Che cosa ti impedisce di usare 'format()' per la formattazione? Questo sembra uno di quei casi in cui è semplicemente più potente. – Joost

risposta

0

Si potrebbe anche fare questo:

[y[0] for y in [x.split(')') for x in s.split('%(')] if len(y)>1] 
+0

Proprio come la regex nella domanda questo fallisce su ''%% (a) s''. – BlackJack

+0

Qual è il requisito esatto allora? Oltre a% (a) s, quali sono gli altri tipi di espressioni che dobbiamo essere in grado di analizzare? %%(come? Qualunque altra cosa? –

0

Non so se questo si qualifica come graziosa nel tuo libro, ma ecco una breve funzione che analizza i nomi . Nessun controllo degli errori, quindi fallirà per le stringhe di formato errato.

def get_names(s): 
    i = s.find('%') 
    while 0 <= i < len(s) - 3: 
     if s[i+1] == '(': 
      yield(s[i+2:s.find(')', i)]) 
     i = s.find('%', i+2) 

string = 'abd %(one) %%(two) 99 %%%(three)' 
list(get_names(string) #=> ['one', 'three'] 
0

Inoltre, è possibile ridurre questo % -task a Formater -solution.

>>> import re 
>>> from string import Formatter 
>>> 
>>> string = '%(a)s and %(b)s are friends.' 
>>> 
>>> string = re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', string) 
>>> 
>>> tuple(fn[1] for fn in Formatter().parse(string) if fn[1] is not None) 
('a', 'b') 
>>> 

In questo caso è possibile utilizzare entrambe le varianti di formattazione, suppongo.

L'espressione regolare in esso dipende da ciò che si desidera.

>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %(c)s friends.') 
'{a} and {b} are {c} friends.' 
>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %%(c)s friends.') 
'{a} and {b} are %%(c)s friends.' 
>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %%%(c)s friends.') 
'{a} and {b} are %%%(c)s friends.' 
3

Per rispondere a questa domanda, è necessario definire "aggraziato". Diversi fattori potrebbero valere la pena di prendere in considerazione:

  1. Il codice è breve, facile da ricordare, facile da scrivere e auto esplicativo?
  2. Riutilizza la logica sottostante (ad esempio, seguire il principio DRY)?
  3. Implementa esattamente la stessa logica di analisi?

Sfortunatamente, la formattazione "%" per le stringhe è implementata nella routine C "PyString_Format" in stringojbect.c. Questa routine non fornisce un'API o hook che consentono l'accesso a un modulo analizzato della stringa di formato. Costruisce semplicemente il risultato mentre analizza la stringa di formato. Pertanto qualsiasi soluzione dovrà duplicare la logica di analisi dalla routine C. Ciò significa che DRY non viene seguito ed espone qualsiasi soluzione alla rottura se viene apportata una modifica alle specifiche di formattazione.

L'algoritmo di parsing in PyString_Format include un bel po 'di complessità, inclusa la gestione delle parentesi nidificate nei nomi delle chiavi, quindi non può essere completamente implementato usando l'espressione regolare né usando la stringa "split()".A parte copiare il codice C da PyString_Format e convertirlo in codice Python, non vedo alcun modo, in modo remoto, di estrarre correttamente i nomi delle chiavi di mappatura sotto tutte le circostanze.

Quindi la mia conclusione è che non esiste un modo "aggraziato" per ottenere i nomi delle chiavi di mappatura per una stringa di formato Python 2.7 "%".

Il codice seguente utilizza un'espressione regolare per fornire una soluzione parziale che copre più comune utilizzo:

import re 
class StringFormattingParser(object): 
    __matcher = re.compile(r'(?<!%)%\(([^)]+)\)[-# +0-9.hlL]*[diouxXeEfFgGcrs]') 
    @classmethod 
    def getKeyNames(klass, formatString): 
     return klass.__matcher.findall(formatString) 

# Demonstration of use with some sample format strings 
for value in [ 
    '%(a)s and %(b)s are friends.', 
    '%%(nomatch)i', 
    '%%', 
    'Another %(matched)+4.5f%d%% example', 
    '(%(should_match(but does not))s', 
    ]: 
    print StringFormattingParser.getKeyNames(value) 

# Note the following prints out "really does match"! 
print '%(should_match(but does not))s' % {'should_match(but does not)': 'really does match'} 

P.S. DRY = Do not Repeat Yourself (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)