Параметры ключ-значение (именованные) в Python: как их использовать

задняя часть Python программист Программа перевода самородков

Аргументы ключ-значение — это особенность Python, которая может показаться странной любому, кто перешел на Python с другого языка программирования. Когда люди изучают Python, часто требуется много времени, чтобы понять различные особенности параметров «ключ-значение».

При обучении Python мне часто хотелось в нескольких словах объяснить богатство параметров «ключ-значение». Я надеюсь, что эта статья может достичь этого эффекта.

В этой статье я объясню, что такое параметры ключ-значение и почему они используются. Затем я дам еще несколько подробных советов по использованию, которые могут упустить из виду даже опытные программисты на Python, поскольку в последних версиях Python 3 многое изменилось. Если вы уже являетесь опытным программистом на Python, вы можете сразу перейти к концу.

Что такое параметры ключ-значение?

Давайте посмотрим, что такое параметры "ключ-значение" (также называемые именованными параметрами).

Взгляните на следующую функцию Python:

from math import sqrt

def quadratic(a, b, c):
    x1 = -b / (2*a)
    x2 = sqrt(b**2 - 4*a*c) / (2*a)
    return (x1 + x2), (x1 - x2)

Когда мы вызываем эту функцию, у нас есть два разных способа передать эти три параметра.

Мы можем передавать значения в качестве параметров-заполнителей следующим образом:

>>> quadratic(31, 93, 62)
(-1.0, -2.0)

Или как параметр ключ-значение, например:

>>> quadratic(a=31, b=93, c=62)
(-1.0, -2.0)

При передаче значений в качестве заполнителей порядок параметров имеет значение:

>>> quadratic(31, 93, 62)
(-1.0, -2.0)
>>> quadratic(62, 93, 31)
(-0.5, -1.0)

Но добавление имени параметра не имеет значения:

>>> quadratic(a=31, b=93, c=62)
(-1.0, -2.0)
>>> quadratic(c=62, b=93, a=31)
(-1.0, -2.0)

Когда мы используем ключ-значение/именованные параметры, важно имя параметра, а не его позиция:

>>> quadratic(a=31, b=93, c=62)
(-1.0, -2.0)
>>> quadratic(c=31, b=93, a=62)
(-0.5, -1.0)

Таким образом, в отличие от многих других языков программирования, Python знает имена параметров, которые получают функции.

Если мы используем вспомогательную функцию, Python сообщит нам имена трех аргументов:

>>> help(quadratic)
Help on function quadratic in module __main__:

quadratic(a, b, c)

Обратите внимание, что функции можно вызывать, используя сочетание заполнителей и имен:

>>> quadratic(31, 93, c=62)
(-1.0, -2.0)

Это действительно удобно, но было бы понятнее использовать полные параметры-заполнители или полные параметры ключ-значение в функции, подобной той, которую мы написали.

Зачем использовать параметры ключ-значение?

При вызове функций в Python обычно приходится выбирать между аргументами ключ-значение и аргументами-заполнителями. Использование параметров ключ-значение может сделать вызовы функций более явными.

Взгляните на этот код:

def write_gzip_file(output_file, contents):
    with GzipFile(None, 'wt', 9, output_file) as gzip_out:
        gzip_out.write(contents)

Эта функция получаетoutput_fileфайловый объект иcontentsstring, затем записывает сжатую gzip-строку в выходной файл.

Следующий код делает то же самое, но заменяет параметры-заполнители параметрами "ключ-значение":

def write_gzip_file(output_file, contents):
    with GzipFile(fileobj=output_file, mode='wt', compresslevel=9) as gzip_out:
        gzip_out.write(contents)

Вы можете видеть, что вызов этого метода с параметрами ключ-значение может более четко увидеть значение этих трех параметров.

Мы опустили здесь параметр. Первый параметр представляетfilename, и естьNoneзначение по умолчанию для . Здесь нам не нужноfilename, потому что мы должны передавать только файловый объект или просто имя файла вGzipFile, не оба.

Есть еще один параметр, который мы можем опустить.

Все еще исходный код, но на этот раз степень сжатия была удалена, а значение по умолчанию9заменять:

def write_gzip_file(output_file, contents):
    with GzipFile(fileobj=output_file, mode='wt') as gzip_out:
        gzip_out.write(contents)

Из-за использования именованных параметров мы смогли отбросить два параметра и расположить оставшиеся 2 параметра в разумном порядке (файловый объект более важен, чем режим выборки «wt»).

Когда мы используем параметры ключ-значение:

  1. Мы можем удалить параметры со значениями по умолчанию
  2. Мы можем изменить параметры в более читаемом виде
  3. Легче понять значение параметров, называя их по имени

где посмотреть функции ключ-значение

Вы можете увидеть параметры ключ-значение во многих местах Python.

В Python есть некоторые функции, которые принимают неограниченное количество аргументов-заполнителей. Эти функции иногда могут получать параметры, которые используются для настройки функциональности. Эти параметры должны использовать именованные параметры, чтобы отличать их от бесконечного числа параметров-заполнителей.

Встроенныйprintнеобязательные атрибуты функцийsep,end,fileиflush, который может принимать только параметры "ключ-значение":

>>> print('comma', 'separated', 'words', sep=', ')
comma, separated, words

itertools.zip_longestфункциональныйfillvalueсвойство (по умолчаниюNone), который также принимает только параметры "ключ-значение":

>>> from itertools import zip_longest
>>> list(zip_longest([1, 2], [7, 8, 9], [4, 5], fillvalue=0))
[(1, 7, 4), (2, 8, 5), (0, 9, 0)]

На самом деле, некоторые функции в Python заставляют аргументы быть названными, хотя и в заполнителях.Можетчетко указано.

В Питоне 2,sortedФункции могут получать параметры в виде заполнителей или ключей-значений:

>>> sorted([4, 1, 8, 2, 7], None, None, True)
[8, 7, 4, 2, 1]
>>> sorted([4, 1, 8, 2, 7], reverse=True)
[8, 7, 4, 2, 1]

Но в Питоне 3sortedТребовать, чтобы все параметры после итератора были указаны как ключ-значение:

>>> sorted([4, 1, 8, 2, 7], None, True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: must use keyword argument for key function
>>> sorted([4, 1, 8, 2, 7], reverse=True)
[8, 7, 4, 2, 1]

В стандартных и сторонних библиотеках распространены не только встроенные функции Python, но и параметры ключ-значение.

сделайте ваши параметры названными

используя*оператор для сопоставления всех аргументов-заполнителей, а затем в*Указав необязательные параметры "ключ-значение", вы можете создать функцию, которая принимает любое количество параметров-заполнителей и определенное количество параметров "ключ-значение".

Вот пример:

def product(*numbers, initial=1):
    total = initial
    for n in numbers:
        total *= n
    return total

Уведомление: если вы не видели его раньше*синтаксис,*numbersвойдет во всеproductЗамещающие параметры функции помещаются вnumbersКортеж, на который указывает переменная.

в функции вышеinitialПараметры должны быть указаны как ключи-значения:

>>> product(4, 4)
16
>>> product(4, 4, initial=1)
16
>>> product(4, 5, 2, initial=3)
120

УведомлениеinitialЕсть значение по умолчанию. Вы также можете указать с помощью этого синтаксисаобязательныйПараметры "ключ-значение":

def join(*iterables, joiner):
    if not iterables:
        return
    yield from iterables[0]
    for iterable in iterables[1:]:
        yield joiner
        yield from iterable

joinerПеременная не имеет значения по умолчанию, поэтому ее необходимо указать:

>>> list(join([1, 2, 3], [4, 5], [6, 7], joiner=0))
[1, 2, 3, 0, 4, 5, 0, 6, 7]
>>> list(join([1, 2, 3], [4, 5], [6, 7], joiner='-'))
[1, 2, 3, '-', 4, 5, '-', 6, 7]
>>> list(join([1, 2, 3], [4, 5], [6, 7]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: join() missing 1 required keyword-only argument: 'joiner'

Следует отметить, что этот вид параметра находится в*Последний синтаксис действителен только в Python 3. В Python 2 нет синтаксиса, требующего именования параметров.

Принимает только параметры "ключ-значение", а не параметры-заполнители.

Что делать, если вы хотите получать только параметры "ключ-значение" без каких-либо параметров-заполнителей?

Если вы хотите получить параметр "ключ-значение" и не планируете получать*параметры заполнителя, вы можете*без каких-либо символов после него.

Например, вот модифицированный Djangodjango.shortcuts.renderфункция:

def render(request, template_name, context=None, *, content_type=None, status=None, using=None):
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

с Джанго сейчасrenderРеализация функции отличается, эта версия не позволяет вызывать таким образом, чтобы все параметры были указаны как заполнители.render.context_type,statusиusingпараметр должен пройти名称указать.

>>> render(request, '500.html', {'error': error}, status=500)
<HttpResponse status_code=500, "text/html; charset=utf-8">
>>> render(request, '500.html', {'error': error}, 500)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: render() takes from 2 to 3 positional arguments but 4 were given

Как и в случае с неограниченным количеством параметров-заполнителей, эти параметры «ключ-значение» также могут потребоваться. Вот функция с четырьмя обязательными параметрами ключ-значение:

from random import choice, shuffle
UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
LOWERCASE = UPPERCASE.lower()
DIGITS = "0123456789"
ALL = UPPERCASE + LOWERCASE + DIGITS

def random_password(*, upper, lower, digits, length):
    chars = [
        *(choice(UPPERCASE) for _ in range(upper)),
        *(choice(LOWERCASE) for _ in range(lower)),
        *(choice(DIGITS) for _ in range(digits)),
        *(choice(ALL) for _ in range(length-upper-lower-digits)),
    ]
    shuffle(chars)
    return "".join(chars)

Эта функция требует, чтобы все функции были указаны по имени:

>>> random_password(upper=1, lower=1, digits=1, length=8)
'oNA7rYWI'
>>> random_password(upper=1, lower=1, digits=1, length=8)
'bjonpuM6'
>>> random_password(1, 1, 1, 8)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: random_password() takes 0 positional arguments but 4 were given

Требование именованных параметров для более явного вызова функций.

Смысл вызова функции такой:

>>> password = random_password(upper=1, lower=1, digits=1, length=8)

Это понятнее, чем называть это так:

>>> password = random_password(1, 1, 1, 8)

Опять же, этот синтаксис работает только в Python 3.

Соответствие параметрам "ключ-значение" с подстановочными знаками

Как написать функцию, которая соответствует любому количеству аргументов ключ-значение?

Например, метод форматирования строки принимает любой аргумент ключ-значение, который вы ему передаете:

>>> "My name is {name} and I like {color}".format(name="Trey", color="purple")
'My name is Trey and I like purple'

Как написать такую ​​функцию?

Python позволяет функциям сопоставлять произвольные входные аргументы ключ-значение, определяя функцию с помощью**оператор:

def format_attributes(**attributes):
    """Return a string of comma-separated key-value pairs."""
    return ", ".join(
        f"{param}: {value}"
        for param, value in attributes.items()
    )

**оператор позволяетformat_attributesФункция принимает любое количество аргументов ключ-значение. Входные параметры будут храниться в файле с именемattributesв словаре.

Вот пример использования нашей функции:

>>> format_attributes(name="Trey", website="http://treyhunner.com", color="purple")
'name: Trey, website: http://treyhunner.com, color: purple'

Вызов функции с подстановочными аргументами ключ-значение

Точно так же, как вы можете определять функции для получения параметров "ключ-значение" с подстановочными знаками, вы также можете передавать параметры "ключ-значение" с подстановочными знаками при вызове функции.

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

Здесь мы вручную извлекаем пары ключ/значение из словаря и передаем их в функцию как аргументы ключ-значение:

>>> items = {'name': "Trey", 'website': "http://treyhunner.com", 'color': "purple"}
>>> format_attributes(name=items['name'], website=items['website'], color=items['color'])
'name: Trey, website: http://treyhunner.com, color: purple'

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

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

>>> items = {'name': "Trey", 'website': "http://treyhunner.com", 'color': "purple"}
>>> format_attributes(**items)
'name: Trey, website: http://treyhunner.com, color: purple'

Эта практика передачи подстановочных параметров ключ-значение в функцию и получения подстановочных параметров внутри функции (как мы делали ранее) особенно распространена при использовании наследования классов:

def my_method(self, *args, **kwargs):
    print('Do something interesting here')
    super().my_method(*args, **kwargs)  # 使用传入的参数调用父类的方法

Уведомление: Точно так же мы можем использовать*оператор для сопоставления и распаковки аргументов-заполнителей.

чувствительность порядка

Начиная с Python 3.6, функции будут поддерживать порядок, в котором передаются аргументы ключ-значение (см.PEP 468). Это означает, что при использовании**При сопоставлении параметров ключ-значение ключи словаря, используемого для хранения результата, будут в том же порядке, что и переданные параметры.

Итак, после Python 3.6 вы будетебольше никогдаПосмотрите что-то вроде этого:

>>> format_attributes(name="Trey", website="http://treyhunner.com", color="purple")
'website: http://treyhunner.com, color: purple, name: Trey'

Соответственно, в Python 3.6+ аргументы всегда хранятся в том порядке, в котором они были переданы:

>>> format_attributes(name="Trey", website="http://treyhunner.com", color="purple")
'name: Trey, website: http://treyhunner.com, color: purple'

Обобщение аргументов ключ-значение в Python

параметрМесто расположенияПередаваемое сообщение обычно не так хорошо, какназваниеэффективный. Поэтому при вызове функции подумайте о том, чтобы дать своим параметрам имена, если это сделает ее значение более понятным.

При определении новой функции забудьте о том, какой параметр следует указывать в качестве параметра "ключ-значение". использовать*Оператор указывает эти параметры как параметры ключ-значение.

Помните, что вы можете использовать**оператор для приема и передачи аргументов ключ-значение с подстановочными знаками.

Важные объекты должны иметь имена, вы можете использовать параметры ключ-значение, чтобы присвоить имена своим объектам!

Нравится мой стиль преподавания?

Хотите узнать больше о Python? Каждую неделю я делюсь своими любимыми ресурсами Python и отвечаю на вопросы, связанные с Python, в чате. Зарегистрируйтесь ниже, и я отвечутвоя проблемаИ научит вас, как сделать ваш код Python более ярким и понятным, более Pythonic.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из ИнтернетаНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллекти другие поля, если вы хотите видеть больше качественных переводов, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.