Главная страница
Навигация по странице:

  • Примеры произвольного числа аргументов

  • Сбор аргументов в коллекцию В первом случае, в определении функции, выполняется сборка лишних позиционных аргументов в кортеж:>>> def f(*args): print args

  • Извлечение аргументов из коллекции

  • Комбинирование ключей и значений по умолчанию

  • Более полезный пример: универсальные функции

  • Математический анализ. 3е издание


    Скачать 4.86 Mb.
    Название3е издание
    АнкорМатематический анализ
    Дата04.02.2022
    Размер4.86 Mb.
    Формат файлаpdf
    Имя файлаpython_01.pdf
    ТипДокументы
    #351981
    страница52 из 98
    1   ...   48   49   50   51   52   53   54   55   ...   98
    f(1, 4)
    1 4 3
    >>> f(1, 4, 5)
    1 4 5
    Наконец, ниже приводится пример взаимодействия режимов переда
    чи значений по ключу и по умолчанию. Поскольку ключи могут нару
    шать обычный порядок отображения аргументов слева направо, они,
    по сути, позволяют «перепрыгивать» через аргументы со значениями по умолчанию:
    >>> f(1, c=6)
    1 2 6
    Здесь значение 1 будет сопоставлено с аргументом по позиции, аргу
    мент c получит значение 6, а аргумент b, в середине, – значение по умолчанию 2.
    Не путайте синтаксические конструкции name=value в заголовке функ
    ции и в вызове функции – в вызове она означает использование режима сопоставления значения с именем аргумента по ключу, а в заголовке

    428
    Глава 16. Области видимости и аргументы определяет значение по умолчанию для необязательного аргумента.
    В обоих случаях это не инструкция присваивания – это особая синтак
    сическая конструкция для этих двух случаев, которая модифицирует механику сопоставления значений с именами аргументов, используе
    мую по умолчанию.
    Примеры произвольного числа аргументов
    Последние два расширения * и ** механизма сопоставления аргумен
    тов и их значений предназначены для поддержки возможности пере
    дачи произвольного числа аргументов функциям. Оба варианта могут появляться как в определениях функций, так и в их вызовах, и в обо
    их случаях они имеют сходные назначения.
    Сбор аргументов в коллекцию
    В первом случае, в определении функции, выполняется сборка лиш
    них позиционных аргументов в кортеж:
    >>> def f(*args): print args
    При вызове этой функции интерпретатор Python соберет все позици
    онные аргументы в новый кортеж и присвоит этот кортеж переменной args
    . Это будет обычный объект кортежа, поэтому из него можно из
    влекать элементы по индексам, выполнять обход в цикле for и т. д.:
    >>> f()
    ()
    >>> f(1)
    (1,)
    >>> f(1,2,3,4)
    (1, 2, 3, 4)
    Комбинация ** дает похожий результат, но применяется при передаче аргументов по ключам – в этом случае аргументы будут собраны в но
    вый словарь, который можно обрабатывать обычными инструмента
    ми, предназначенными для работы со словарями. В определенном смысле форма ** позволяет преобразовать аргументы, передаваемые по ключам, в словари, которые можно будет обойти с помощью метода keys
    , итераторов словарей и т. д.:
    >>> def f(**args): print args
    >>> f()
    { }
    >>> f(a=1, b=2)
    {'a': 1, 'b': 2}
    Наконец, в заголовках функций можно комбинировать обычные аргу
    менты, * и ** для реализации чрезвычайно гибких сигнатур вызова:
    >>> def f(a, *pargs, **kargs): print a, pargs, kargs

    Специальные режимы сопоставления аргументов
    429
    >>> f(1, 2, 3, x=1, y=2)
    1 (2, 3) {'y': 2, 'x': 1}
    Фактически все эти формы передачи аргументов можно объединять в еще более сложные комбинации, которые на первый взгляд могут по
    казаться неоднозначными – к этой идее мы еще вернемся ниже, в этой главе. А сейчас посмотрим, как используются формы * и ** в вызовах функций.
    Извлечение аргументов из коллекции
    В последних версиях Python форму * можно также использовать в вы
    зовах функций. В этом случае данная форма передачи аргументов име
    ет противоположный смысл по сравнению с применением этой формы в определениях функций – она распаковывает, а не создает коллекцию аргументов. Например, можно передать в функцию четыре аргумента в виде кортежа и позволить интерпретатору распаковать их в отдель
    ные аргументы:
    >>> def func(a, b, c, d): print a, b, c, d
    >>> args = (1, 2)
    >>> args += (3, 4)
    >>> func(*args)
    1 2 3 4
    Точно также форма ** в вызовах функций распаковывает словари пар ключ/значение в отдельные аргументы, которые передаются по ключу:
    >>> args = {'a': 1, 'b': 2, 'c': 3}
    >>> args['d'] = 4
    >>> func(**args)
    1 2 3 4
    Здесь также можно очень гибко комбинировать в одном вызове обыч
    ные позиционные аргументы и аргументы по ключу:
    >>> func(*(1, 2), **{'d': 4, 'c': 4})
    1 2 4 4
    >>> func(1, *(2, 3), **{'d': 4})
    1 2 3 4
    >>> func(1, c=3, *(2,), **{'d': 4})
    1 2 3 4
    Такого рода программный код удобно использовать, когда заранее не
    возможно предсказать число аргументов, которые могут быть переда
    ны функции – вы можете собирать коллекцию аргументов во время ис
    полнения и вызывать функцию таким способом. Не путайте синтакси
    ческие конструкции */** в заголовках функций и в их вызовах – в за
    головке они означают сбор всех лишних аргументов в коллекцию,
    а в вызове выполняют распаковку коллекций в отдельные аргументы.

    430
    Глава 16. Области видимости и аргументы
    Мы еще вернемся к этим формам в следующей главе, когда будем рас
    сматривать встроенную функцию apply (для замены которой, в значи
    тельной степени, предназначены эти конструкции).
    Комбинирование ключей и значений по умолчанию
    Ниже приводится немного больший по объему пример, демонстрирую
    щий передачу аргументов по ключам и по умолчанию в действии. В этом примере вызывающая программа всегда должна передавать функции как минимум два аргумента (spam и eggs), два других аргумента явля
    ются необязательными. В случае их отсутствия интерпретатор присво
    ит именам toast и ham значения по умолчанию, указанные в заголовке:
    def func(spam, eggs, toast=0, ham=0): # Первые 2 являются обязательными
    print (spam, eggs, toast, ham)
    func(1, 2) # Выведет: (1, 2, 0, 0)
    func(1, ham=1, eggs=0) # Выведет: (1, 0, 0, 1)
    func(spam=1, eggs=0) # Выведет: (1, 0, 0, 0)
    func(toast=1, eggs=2, spam=3) # Выведет: (3, 2, 1, 0)
    func(1, 2, 3, 4) # Выведет: (1, 2, 3, 4)
    Обращаю снова ваше внимание: когда в вызовах используются ключи аргументов, порядок следования аргументов не имеет значения, пото
    му что сопоставление выполняется по именам, а не по позициям. Вы
    зывающая программа обязана передать значения для аргументов spam и eggs, а сопоставление может выполняться как по позиции, так и по ключам. Обратите также внимание на то, что форма name=value имеет разный смысл в вызове функции и в инструкции def (ключ – в вызове и значение по умолчанию – в заголовке).
    Функция поиска минимума
    Чтобы придать обсуждению больше конкретики, выполним упражне
    ние, которое демонстрирует практическое применение механизмов со
    поставления аргументов. Предположим, что вам необходимо написать функцию, которая способна находить минимальное значение из про
    извольного множества аргументов с произвольными типами данных.
    То есть функция должна принимать ноль или более аргументов –
    столько, сколько вы пожелаете передать. Более того, функция должна работать со всеми типами объектов, имеющимися в языке Python: чис
    лами, строками, списками, списками словарей, файлами и даже None.
    Первое требование представляет собой обычный пример того, как мож
    но найти применение форме * – мы можем собирать аргументы в кор
    теж и выполнять их обход с помощью простого цикла for. Второе тре
    бование тоже не представляет никакой сложности: все типы объектов поддерживают операцию сравнения, поэтому нам не требуется учиты
    вать типы объектов в функции (полиморфизм в действии) – мы можем просто слепо сравнивать объекты и позволить интерпретатору само
    стоятельно выбрать корректную операцию сравнения.

    Специальные режимы сопоставления аргументов
    431
    Основное задание
    Ниже представлены три способа реализации этой функции, из кото
    рых, по крайней мере, один был предложен студентом:

    Первая версия функции извлекает первый аргумент (args – это кор
    теж) и обходит остальную часть коллекции, отсекая первый эле
    мент (нет никакого смысла сравнивать объект сам с собой, особен
    но, если это довольная крупная структура данных).

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

    Третья версия преобразует кортеж в список с помощью встроенной функции list и использует метод списка sort.
    Метод sort написан на языке C, поэтому иногда он может обеспечивать более высокую производительность по сравнению с другими версиями функции, но линейный характер сканирования в первых двух версиях в большинстве случаев обеспечивает им более высокую скорость.
    1
    Файл
    mins.py
    содержит реализацию всех трех решений:
    def min1(*args):
    res = args[0]
    for arg in args[1:]:
    if arg < res:
    res = arg return res def min2(first, *rest):
    for arg in rest:
    if arg < first:
    first = arg return first
    1
    В действительности все очень не просто. Функция sort в языке Python на
    писана на языке C и реализует высокоэффективный алгоритм, который пытается использовать существующий порядок следования сортируемых элементов. Этот алгоритм называется «timsort» в честь его создателя Тима
    Петерса (Tim Peters), а в его документации говорится, что время от време
    ни он показывает «сверхъестественную производительность» (очень не
    плохую для сортировки!). Однако сортировка по своей природе является экспоненциальной операцией (в процессе сортировки необходимо делить последовательность на составляющие и снова объединять их много раз),
    тогда как другие версии используют линейные алгоритмы сканирования слева направо. В результате сортировка выполняется быстрее, когда эле
    менты последовательности частично упорядочены, и медленнее в других случаях. Даже при всем при этом производительность самого интерпрета
    тора может изменяться по времени и тот факт, что сортировка реализована на языке C, может существенно помочь. Для более точного анализа произ
    водительности вы можете использовать модули time и timeit, с которыми мы познакомимся в следующей главе.

    432
    Глава 16. Области видимости и аргументы def min3(*args):
    tmp = list(args) # Или, в Python 2.4+: return sorted(args)[0]
    tmp.sort()
    return tmp[0]
    print min1(3,4,1,2)
    print min2("bb", "aa")
    print min3([2,2], [1,1], [3,3])
    Все три решения дают одинаковые результаты. Попробуйте несколько раз вызвать функции в интерактивной оболочке, чтобы поэксперимен
    тировать с ними самостоятельно:
    % python mins.py
    1
    aa
    [1, 1]
    Обратите внимание, что ни один из этих трех вариантов не выполняет проверку ситуации, когда функции не передается ни одного аргумен
    та. Такую проверку можно было бы предусмотреть, но в этом нет ника
    кой необходимости – во всех трех решениях интерпретатор автомати
    чески возбудит исключение, если та или иная функция не получит ни одного аргумента. В первом случае исключение будет возбуждено при попытке получить нулевой элемент; во втором – когда обнаружится несоответствие списка аргументов; и в третьем – когда функция попы
    тается вернуть нулевой элемент.
    Это именно то, что нам нужно, потому что эти функции поддерживают поиск среди данных любого типа и не существует какогото особого зна
    чения, которое можно было бы вернуть в качестве признака ошибки.
    Из этого правила есть свои исключения (например, когда приходится выполнить дорогостоящие действия, прежде чем появится ошибка), но вообще лучше исходить из предположения, что аргументы не будут вы
    зывать ошибок в работе программного кода функции, и позволить ин
    терпретатору возбуждать исключения, когда этого не происходит.
    Дополнительные баллы
    Студенты и читатели могут получить дополнительные баллы, если из
    менят эти функции так, что они будут отыскивать не минимальное,
    а максимальное значение. Сделать это достаточно просто: в первых двух версиях достаточно заменить < на >, а третья версия должна воз
    вращать не элемент tmp[0], а элемент tmp[1]. Дополнительные баллы будут начислены тем, кто догадается изменить имя функции на «max»
    (хотя это совершенно необязательно).
    Кроме того, вполне возможно обобщить функцию так, что она будет отыскивать либо минимальное, либо максимальное значение, опреде
    ляя отношения элементов за счет интерпретации строки выражения с помощью таких средств, как встроенная функция eval (подробности в руководстве к библиотеке), или передавая произвольную функцию

    Специальные режимы сопоставления аргументов
    433
    сравнения. В файле minmax.py содержится реализация последнего ва
    рианта:
    def minmax(test, *args):
    res = args[0]
    for arg in args[1:]:
    if test(arg, res):
    res = arg return res def lessthan(x, y): return x < y # См. также: lambda
    def grtrthan(x, y): return x > y print minmax(lessthan, 4, 2, 1, 5, 6, 3) # Тестирование
    print minmax(grtrthan, 4, 2, 1, 5, 6, 3)
    % python minmax.py
    1 6
    Функции – это одна из разновидностей объектов, которые могут пере
    даваться в функции, как в этом случае. Например, чтобы заставить функцию отыскивать максимальное (или любое другое) значение, мы могли бы просто передать ей нужную функцию test. На первый взгляд может показаться, что мы делаем лишнюю работу, однако главное преимущество такого обобщения функций (вместо того, чтобы содер
    жать две версии, отличающиеся единственным символом) заключает
    ся в том, что в будущем нам может потребоваться изменить одну функ
    цию, а не две.
    Заключение
    Конечно, это было всего лишь упражнение. В действительности нет никаких причин создавать функции min и max, потому что обе они уже имеются в языке Python! Встроенные версии функций работают прак
    тически так же, как и наши, но они написаны на языке C для получе
    ния более высокой скорости работы.
    Более полезный пример: универсальные функции
    Теперь рассмотрим более полезный пример использования специаль
    ных режимов сопоставления аргументов. В конце предыдущей главы мы написали функцию, которая возвращала пересечение двух после
    довательностей (она отбирала элементы, общие для обеих последова
    тельностей). Ниже приводится версия функции, которая возвращает пересечение произвольного числа последовательностей (одной или бо
    лее), где используется механизм передачи произвольного числа аргу
    ментов в форме *args для сбора всех передаваемых аргументов в виде коллекции. Все аргументы передаются в тело функции в составе корте
    жа, поэтому для их обработки можно использовать простой цикл for.
    Ради интереса мы напишем функцию, возвращающую объединение,

    434
    Глава 16. Области видимости и аргументы которая также принимает произвольное число аргументов и собирает вместе все элементы, имеющиеся в любом из операндов:
    def intersect(*args):
    res = []
    for x in args[0]: # Сканировать первую последовательность
    for other in args[1:]: # Во всех остальных аргументах
    if x not in other: break # Общий элемент?
    else: # Нет: прервать цикл
    res.append(x) # Да: добавить элемент в конец
    return res def union(*args):
    res = []
    for seq in args: # Для всех аргументов
    for x in seq: # Для всех элементов
    if not x in res:
    res.append(x) # Добавить новый элемент в результат
    return res
    Поскольку эти функции могут использоваться многократно (и они слишком большие, чтобы вводить их в интерактивной оболочке), мы сохраним их в модуле с именем inter2.py (подробнее о модулях расска
    зывается в пятой части книги). В обе функции аргументы передаются в виде кортежа args. Как и оригинальная версия intersect, обе они ра
    ботают с любыми типами последовательностей. Ниже приводится при
    мер обработки строк, последовательностей разных типов и случай об
    работки более чем двух последовательностей:
    % python
    >>> from inter2 import intersect, union
    >>> s1, s2, s3 = "SPAM", "SCAM", "SLAM"
    >>> intersect(s1, s2), union(s1, s2) # Два операнда
    (['S', 'A', 'M'], ['S', 'P', 'A', 'M', 'C'])
    >>> intersect([1,2,3], (1,4)) # Смешивание типов
    [1]
    >>> intersect(s1, s2, s3) # Три операнда
    ['S', 'A', 'M']
    >>> union(s1, s2, s3)
    ['S', 'P', 'A', 'M', 'C', 'L']
    Следует заметить, что в языке Python появился новый тип дан
    ных – множества (описывается в главе 5), поэтому, строго говоря,
    ни одна из этих функций больше не требуется, – они включены в книгу только для демонстрации подходов к программированию функций. (Так как Python постоянно улучшается, наблюдается странная тенденция – мои книжные примеры делаются устарев
    шими с течением времени!)

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

    В вызове функции все аргументы, которые передаются не по ключу
    (name), должны находиться в начале списка, далее должны следо
    вать аргументы, которые передаются по ключу (name=value), вслед за ними должна следовать форма *name и, наконец, **name.

    В заголовке функции аргументы должны стоять в том же самом по
    рядке: обычные аргументы (name), вслед за ними аргументы со зна
    чениями по умолчанию (name=value), далее форма *name, если необхо
    димо, и, наконец, форма **name.
    Если поставить аргументы в любом другом порядке, вы получите сооб
    щение о синтаксической ошибке, потому что в этих случаях комбина
    ция получается неоднозначной. Сам интерпретатор использует сле
    дующий порядок сопоставления аргументов перед выполнением опе
    раций присваивания:
    1. Определяются позиционные аргументы, которые передаются не по ключу.
    2. Определяются аргументы, которые передаются по ключу, и произ
    водится сопоставление имен.
    3. Определяются и помещаются в кортеж *name дополнительные аргу
    менты, которые передаются не по ключу.
    4. Определяются и помещаются в словарь **name дополнительные ар
    гументы, которые передаются по ключу.
    5. Определяются аргументы, имеющие значения по умолчанию, кото
    рые отсутствуют в вызове.
    После этого интерпретатор убеждается, что в каждом аргументе пере
    дается единственное значение, в противном возбуждает исключение.
    Это действительно так же сложно, как выглядит, но изучение алгорит
    ма сопоставления аргументов в языке Python поможет вам разобраться с некоторыми сложными случаями, особенно когда одновременно ис
    пользуются несколько режимов. Дополнительные примеры со специ
    альными режимами сопоставления приводятся в упражнениях в конце четвертой части.
    Как видите, режимы сопоставления аргументов могут быть весьма сложными. Но использовать их совершенно необязательно – вы може
    те ограничиться исключительно позиционными аргументами и это,
    пожалуй, будет наилучший выбор. Однако некоторые инструменты языка Python используют эти режимы, поэтому их понимание играет важную роль.

    1   ...   48   49   50   51   52   53   54   55   ...   98


    написать администратору сайта