2016-01-19 24 views
7

Czy istnieje wdzięczny sposób na uzyskanie nazw zmiennych o nazwie %s? Jak to:Jak uzyskać nazwy nazwanych zmiennych z łańcucha python?

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

Znane alternatywne sposoby:

  1. nazwy analizowania wykorzystujące wyrażenia regularnego, np .:

    import re 
    names = re.findall(r'%\((\w)\)[sdf]', string) # ['a', 'b'] 
    
  2. Zastosowanie .format() -Kompatybilny formatowanie i Formatter().parse(string).

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

Ale co z ciągiem zmiennych% s-jak?

PS: Python 2.7

+2

Opisywana metoda działa dobrze. Zwraca ["a", "b"]. Więc czego teraz brakuje? –

+0

@AdiLevin Sposób numer 1 wymaga dodatkowego importu. Sposób numer 2 wymaga innego formatu ciągu. Jestem ciekawy, czy istnieje sposób na uzyskanie tego samego wyniku przy użyciu wewnętrznych metod i właściwości 'string' obiektów lub, być może, niektórych funkcji modułu string. – hackprime

+0

Co uniemożliwia korzystanie z formatu 'format()' do formatowania? Wydaje się, że jest to jeden z tych przypadków, w których jest on po prostu silniejszy. – Joost

Odpowiedz

0

Można też to zrobić:

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

Podobnie jak w przypadku wyrażenia regularnego w pytaniu, to kończy się na '%% (a) s''. – BlackJack

+0

Jakie jest zatem dokładne wymaganie? Poza% (a) s, jakie są inne rodzaje wyrażeń, które musimy móc analizować? %%(tak jak? Coś jeszcze? –

0

Nie wiem, czy to kwalifikuje się jako wdzięku w książce, ale tutaj jest krótka funkcja, która analizuje się nazwy . Brak sprawdzania błędów, więc nie powiedzie się w przypadku zniekształconych ciągów formatów.

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

Ponadto, można zmniejszyć tę % -task do 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') 
>>> 

W tym przypadku można użyć obu wariantów formowania, tak przypuszczam.

Wyrażenie regularne w nim zależy od tego, co chcesz.

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

Aby odpowiedzieć na to pytanie, należy zdefiniować "pełen wdzięku". Warto rozważyć kilka czynników:

  1. Czy kod jest krótki, łatwy do zapamiętania, łatwy do napisania i nie wymaga objaśnień?
  2. Czy ponownie wykorzystuje logikę leżącą u podstaw (tj. Przestrzega zasady DRY)?
  3. Czy implementuje dokładnie tę samą logikę analizowania?

Niestety, formatowanie "%" dla łańcuchów jest zaimplementowane w procedurze C "PyString_Format" w stringojbect.c. Ta procedura nie udostępnia interfejsu API ani haków, które umożliwiają dostęp do przeanalizowanej postaci ciągu formatu. Po prostu buduje wynik podczas analizowania ciągu formatów. Zatem każde rozwiązanie będzie musiało duplikować logikę parsowania z procedury C. Oznacza to, że DRY nie jest przestrzegane i naraża każde rozwiązanie na złamanie, jeśli wprowadzono zmianę w specyfikacji formatowania.

Algorytm parsowania w PyString_Format zawiera spory fragment złożoności, w tym obsługę zagnieżdżonych nawiasów w nazwach kluczy, więc nie można go w pełni zaimplementować za pomocą wyrażeń regularnych ani za pomocą łańcucha "split()".Bez kopiowania kodu C z PyString_Format i konwertowania go na kod Pythona, nie widzę żadnego łatwego sposobu na poprawne wyodrębnienie nazw klawiszy odwzorowania w warunkach wszystkich.

Podsumowując, nie ma "pełnego gracji" sposobu na uzyskanie nazw kluczy mapowania dla łańcucha formatu Python 2.7 "%".

Następujący kod wykorzystuje regularnie ekspresji, aby zapewnić częściowe rozwiązanie obejmuje najczęściej wykorzystywany:

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 = Nie powtarzaj się (https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)