Управление аргументами ключевых слов в строках Python: как отформатировать только некоторые аргументы и оставить другие неформатированными - PullRequest
0 голосов
/ 02 ноября 2018

У меня возникли проблемы с пониманием того, как работает метод format() для строк.

Предположим, что я установил строковую переменную с аргументами ключевых слов:

s = '{hello} {person_name}'

Я мог бы либо присвоить это значение другой переменной, либо распечатать его. В последнем случае результат будет {hello} {person_name}.

Я мог бы также использовать метод format() при печати s и назначить некоторые значения ключевым словам:

print(s.format(hello='hello', person_name='Alice'))

В этом случае результат равен hello Alice. Конечно, я мог бы также назначить его новой переменной.

Моя проблема возникает, когда я хочу использовать формат только для одного ключевого слова:

print(s.format(hello='hello'))

или

a = s.format(hello='hello')

Они оба выдают ошибку:

KeyError: 'person_name'


Я хочу иметь возможность запускать что-то вроде:

s = '{hello} {person_name}'
a = s.format(hello='hello')
if something:
  b = a.format(person_name='Alice')
else:
  b = a.format(person_name='Bob')
print(b)

Возможно ли что-то подобное, или я должен установить все ключевые слова при использовании format()?

Ответы [ 4 ]

0 голосов
/ 02 ноября 2018

Согласно PEP 3101 Если индекс или ключевое слово ссылаются на элемент, который не существует, следует вызвать IndexError / KeyError.

Но вы можете создать свой собственный класс форматирования, подобный этому.

from string import Formatter


class MyStringFormatter(Formatter):
    def get_value(self, key, args, kwds):
        try:
            return super().get_value(key, args, kwds)
        except KeyError:
            return "{%s}" % key

fmt = MyStringFormatter()

DEMO

s = "{hello} {person_name}"

keywords = {'hello': 'hello'}
a = fmt.format(s, **keywords)
print(a) 
# This will print hello {person_name}

something = False

if something:
    person_name = {'person_name': 'Alice'}
else:
    person_name = {'person_name': 'Bob'}
b = fmt.format(a, **person_name)

print(b)   
# This will print `hello Bob` if `something` is False, 'hello Alice' otherwise.
0 голосов
/ 02 ноября 2018

Я думаю, что вы должны определить все ключевые слова при использовании format().

Я бы предложил другой подход, используя *args:

def printHello(*args):
    print(' '.join([arg for arg in args]))

printHello('hello', 'Alice')
# hello Alice
printHello('hello')
# hello

Вы можете отправить любое количество слов в эту функцию.

0 голосов
/ 02 ноября 2018

Возможно ли что-то подобное или я должен установить все ключевые слова, когда использую format ()?

PEP-3101 говорит:

Если индекс или ключевое слово ссылаются на элемент, который не существует, следует вызвать IndexError / KeyError.

Так что да, если вы собираетесь использовать ключевые слова, вам придется указать их все.

0 голосов
/ 02 ноября 2018

В вашем случае вы можете использовать {person} в строке:

# double brace the person_name to escape it for the first format
s = '{hello} {{person_name}}'
a = s.format(hello='hello')

# a = 'hello {person_name}'

if something:
  b = a.format(person_name='Alice')
  # b = 'hello Alice'
else:
  b = a.format(person_name='Bob')
  # b = 'hello Bob'

print(b)

Однако при использовании этого метода вам необходимо следовать явному порядку, в котором вы экранировали свои переменные. то есть вы должны назначить hello сначала , а затем person_name. Если вам нужно проявлять гибкость в отношении порядка вещей, я бы предложил использовать dict для построения переменных перед их полной передачей:

# dict approach
s = '{hello} {person_name}'

# determine the first variable
d = {'hello':'hello'}
... do something
d.update({'person': 'Alice'})

# unpack the dictionary as kwargs into your format method
b = s.format(**d)

# b = 'hello Alice'

Это дает вам немного больше гибкости в порядке вещей. Но вы должны вызывать .format() только один раз все переменные, указанные в вашем dict (по крайней мере, он должен иметь значение по умолчанию), в противном случае это все равно вызовет ошибку.

Если вы хотите проявить больше фантазии и хотите иметь возможность печатать имена полей при отсутствии переменной, вы также можете создать свою собственную функцию-обертку:

# wrapper approach
# We'll make use of regex to keep things simple and versatile
import re

def my_format(message, **kwargs):

    # build a regex pattern to catch words+digits within the braces {}
    pat = re.compile('{[\w\d]+}')

    # build a dictionary based on the identified variables within the message provided
    msg_args = {v.strip('{}'): v for v in pat.findall(message)}

    # update the dictionary with provided keyword args
    msg_args.update(kwargs)

    # ... and of course, print it
    print(message.format(**msg_args))

s = 'Why {hello} there {person}'
my_format(s, hello='hey')
# Why hey there {person}

my_format(s, person='Alice') 
# Why {hello} there Alice

Вы можете определить отображение по умолчанию (при отсутствии переменной), которое хотите, изменив v в словаре.

...