Правильный способ использования ** kwargs в Python - PullRequest
0 голосов
/ 14 сентября 2018

Я посмотрел на этот вопрос , но он не совсем отвечает на мой вопрос.В качестве примера я взял простой способ напечатать свое имя.

def call_me_by_name(first_name):
    print("Your name is {}".format(first_name))

Позже я понял, что по желанию я также хотел бы иметь возможность печатать отчество и фамилию.Я внес следующие изменения, чтобы учесть, что используя ** kwargs, опасаясь, что в будущем меня могут добавить дополнительные поля для самого имени (например, 3-го, 4-го, 5-го имени и т. Д.)

Iрешил использовать ** kwargs

def call_me_by_name(first_name,**kwargs):

    middle_name = kwargs['middle_name'] if kwargs.get('middle_name') else ""
    last_name = kwargs['last_name'] if kwargs.get('last_name') else ""

    print("Your name is {} {} {}".format(first_name,middle_name,last_name))

Моя единственная проблема в том, что, поскольку я продолжаю реализовывать поддержку большего количества имен, я заканчиваю тем, что пишу по одной строке кода для каждого аргумента ключевого слова, который может или не может прийтимой метод.Я хотел бы найти решение, которое будет настолько питонным, насколько возможно.Есть ли лучший способ добиться этого?

РЕДАКТИРОВАТЬ 1

Я хочу использовать ключевые аргументы, так как это всего лишь пример программы.Фактический вариант использования - это анализ файла.Аргументы ключевых слов на данный момент поддерживают синтаксический анализ файла из

1) Определенный байт в файле.
2) Определенный номер строки в файле.

Только одно из этих двух условий может быть установлено в любой данный момент времени (так как невозможно прочитать из определенного байтового смещения в файле и из номера строки одновременно), но можетв будущем будет больше таких условий, таких как синтаксический анализ файла по первому вхождению символа и т. д. Таких условий может быть 10-20 различных, мой метод должен поддерживать, НО только вызывающее лицо может установить только одно из этих условий в любое время,Я не хочу иметь 20-30 различных условий ЕСЛИ, если нет другого выбора.

Ответы [ 6 ]

0 голосов
/ 14 сентября 2018

У вас есть два отдельных вопроса с двумя разными питонскими способами ответа на эти вопросы.

1- Ваша первая проблема заключалась в том, что вы не хотите продолжать добавлять новые строки, чем больше аргументов вы начинаете поддерживать при форматированиистрока.Чтобы обойти это, используйте defaultdict, чтобы вы могли возвращать пустую строку, когда вы не указали конкретный аргумент ключевого слова, и str.format_map, который принимает dict как способ ввода аргументов ключевого слова для форматирования.Таким образом, вам нужно только обновить строку и аргументы ключевых слов, которые вы хотите напечатать:

from collections import defaultdict
def call_me_by_name(**kwargs):
    default_kwargs = defaultdict(str, kwargs)
    print("Your name is {first_name} {second_name} {third_name}".format_map(default_kwargs))

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

ООП:

class FileLookup:

    def parse(self, **kwargs):
        return getattr(self, next(iter(kwargs)))(**kwargs)

    def line_number(self, line_number):
        print('parsing with a line number: {}'.format(line_number))

    def byte_position(self, byte_position):
        print('parsing with a byte position: {}'.format(byte_position))

fl = FileLookup()
fl.parse(byte_position=10)
fl.parse(line_number=10)

Модуль:

def line_number(line_number):
    print('parsing with a line number: {}'.format(line_number))

def byte_position(byte_position):
    print('parsing with a byte position: {}'.format(byte_position))

def parse(**kwargs):
    return globals()[next(iter(kwargs))](**kwargs)

parse(byte_position=29)
parse(line_number=29)
0 голосов
/ 14 сентября 2018

Я думаю, что ваше call_me_by_name не является хорошим примером для ** kwargs. Но если вы хотите избежать пропуска некоторых экзотических, непроверенных полей имени, call_me_by_name может выглядеть так:

def call_me_by_name(first_name, last_name, middle_name='', **kwargs):
    s = "Your name is {} {} {}".format(first_name,middle_name,last_name)
    if kwargs:
        s += " (" + ", ".join(["{}: {}".format(k,v) for k,v in kwargs.items()]) + ")"
    print(s)

Тест:

name = {'first_name': 'Henry', 'last_name': 'Ford', 'ordinal': 'II', 'nickname': 'Hank the Deuce'}
call_me_by_name(**name)
>>> Your name is Henry  Ford (ordinal: II, nickname: Hank the Deuce)
0 голосов
/ 14 сентября 2018

Я опубликую его как ответ:

Вы можете мгновенно распаковать значения kwargs в функцию форматирования следующим образом:

"Your name is {} {} {}".format(first_name , *kwargs)

Но какУпоминается пользователь @ PM 2Ring Вы должны знать, что это не гарантирует, что имена будут в правильном порядке.

0 голосов
/ 14 сентября 2018

Вы можете упростить это:

middle_name = kwargs.get('middle_name', '')
0 голосов
/ 14 сентября 2018

Мне кажется, вам лучше не использовать kwargs и даже не функцию, вы можете просто сделать что-то вроде этого:

print("Your name is", " ".join([first_name, middle_name, last_name]))

Или, если вам нужна функция:

def call_me_by_name(*args):
     print("Your name is", " ".join(args))
0 голосов
/ 14 сентября 2018

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

def call_me_by_name(first_name, middle_name="", last_name=""):
    print("Your name is {} {} {}".format(first_name,middle_name,last_name))

Это лучше для случаев, когда вы хотите что-то вроде «мешочка» опций.Например,

def configure(**kwargs):
    if 'color' in kwargs:
        set_color(kwargs['color'])

и т. Д.

...