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

  • Передача параметров и возвращаемые значения

  • Правила видимости

  • Функции как объекты и замыкания

  • справочник по Python. мм isbn 9785932861578 9 785932 861578


    Скачать 4.21 Mb.
    Названиемм isbn 9785932861578 9 785932 861578
    Анкорсправочник по Python
    Дата08.05.2022
    Размер4.21 Mb.
    Формат файлаpdf
    Имя файлаBizli_Python-Podrobnyy-spravochnik.440222.pdf
    ТипСправочник
    #518195
    страница11 из 82
    1   ...   7   8   9   10   11   12   13   14   ...   82
    Глава
    6
    .
    Функции и функциональное
    программирование
    В большинстве своем программы разбиты на функции – для достижения большей модульности и упрощения обслуживания. Язык Python не только облегчает объявление функций, но и снабжает их удивительным количе- ством дополнительных особенностей, унаследованных из функциональ- ных языков программирования. В этой главе описываются функции, пра- вила видимости, замыкания, декораторы, генераторы, сопрограммы и дру- гие особенности, присущие функциональному программированию. Кроме того, здесь же описываются генераторы списков и выражения-генераторы, которые являются мощными инструментами программирования в декла- ративном стиле и обработки данных.
    Функции
    Функции объявляются с помощью инструкции def:
    def add(x,y):
    return x + y
    Тело функции – это простая последовательность инструкций, которые выполняются при вызове функции. Вызов функции осуществляется как обращение к имени функции, за которым следует кортеж аргументов, на- пример: a = add(3,4). Порядок следования аргументов в вызове должен со- впадать с порядком следования аргументов в определении функции. Если будет обнаружено несоответствие, интерпретатор возбудит исключение
    TypeError
    Аргументы функции могут иметь значения по умолчанию. Например:
    def split(line,delimiter=’,’):
    инструкции
    Если в объявлении функции присутствует аргумент со значением по умол- чанию, этот аргумент и все следующие за ним считаются необязательны-

    Функции
    131
    ми. Если значения по умолчанию назначены не всем необязательным аргу- ментам, возбуждается исключение SyntaxError.
    Значения аргументов по умолчанию всегда связываются с теми самыми объектами, которые использовались в качестве значений в объявлении функции. Например:
    a = 10
    def foo(x=a):
    return x
    ёё
    a = 5 # Изменить значение ‘a’.
    foo() # вернет 10 (значение по умолчанию не изменилось)
    При этом использование изменяемых объектов в качестве значений по умолчанию может приводить к неожиданным результатам:
    def foo(x, items=[]):
    items.append(x)
    return items
    ёё
    foo(1) # вернет [1]
    foo(2) # вернет [1, 2]
    foo(3) # вернет [1, 2, 3]
    Обратите внимание, что аргумент по умолчанию запоминает изменения в результате предыдущих вызовов. Чтобы предотвратить такое поведение, в качестве значения по умолчанию лучше использовать None и добавить проверку, как показано ниже:
    def foo(x, items=None):
    if items is None:
    items = []
    items.append(x)
    return items
    Если перед последним аргументом в определении функции добавить сим- вол звездочки (*), функция сможет принимать переменное число аргумен- тов:
    def fprintf(file, fmt, *args):
    file.write(fmt % args)
    ёё
    # Вызов функции fprintf.
    # Аргумент args получит значение (42,”hello world”, 3.45)
    fprintf(out,”%d %s %f”, 42, “hello world”, 3.45)
    В данном случае все дополнительные аргументы будут помещены в пере- менную args в виде кортежа. Чтобы передать кортеж args другой функции, как если бы это был набор аргументов, достаточно использовать синтаксис
    *args
    , как показано ниже:
    def printf(fmt, *args):
    # Вызвать другую функцию и передать ей аргумент args fprintf(sys.stdout, fmt, *args)

    132
    Глава 6. Функции и функциональное программирование
    Кроме того, имеется возможность передавать функциям аргументы, явно указывая их имена и значения. Такие аргументы называются именован-
    ными аргументами
    . Например:
    def foo(w,x,y,z):
    инструкции
    ёё
    # Вызов с именованными аргументами foo(x=3, y=22, w=’hello’, z=[1,2])
    При вызове с именованными аргументами порядок следования аргумен- тов не имеет значения. Однако при этом должны быть явно указаны име- на всех обязательных аргументов, если только они не имеют значений по умолчанию. Если пропустить какой-либо из обязательных аргументов или использовать именованный аргумент, не совпадающий ни с одним из имен параметров в определении функции, будет возбуждено исключение TypeEr- ror
    . Кроме того, так как в языке Python любая функция может быть вызва-
    Python любая функция может быть вызва- любая функция может быть вызва- на с использованием именованных аргументов, то резонно придерживать- ся правила – объявлять функции с описательными именами аргументов.
    В одном вызове функции допускается одновременно использовать позици- онные и именованные аргументы, при соблюдении следующих условий: первыми должны быть указаны позиционные аргументы, должны быть определены значения для всех обязательных аргументов и ни для одного из аргументов не должно быть передано более одного значения. Например:
    foo(‘hello’, 3, z=[1,2], y=22)
    foo(3, 22, w=’hello’, z=[1,2]) # TypeError. Несколько значений для w
    Если последний аргумент в объявлении функции начинается с символов **, все дополнительные именованные аргументы (имена которых отсутствуют в объявлении функции) будут помещены в словарь и переданы функции.
    Это обеспечивает удобную возможность писать функции, способные при- нимать значительное количество параметров, описание которых в объяв- лении функции выглядело бы слишком громоздко. Например:
    def make_table(data, **parms):
    # Получить параметры из аргумента parms (словарь)
    fgcolor = parms.pop(“fgcolor”,”black”)
    bgcolor = parms.pop(“bgcolor”,”white”)
    width = parms.pop(“width”,None)
    ...
    # Нет больше параметров if parms:
    raise TypeError(“Неподдерживаемые параметры %s” % list(parms))
    ёё
    make_table(items, fgcolor=”black”, bgcolor=”white”, border=1,
    borderstyle=”grooved”, cellpadding=10,
    width=400)
    Допускается одновременно использовать дополнительные именованные аргументы и список позиционных аргументов переменной длины, при условии, что аргумент, начинающийся с символов **, указан последним:

    Передача параметров и возвращаемые значения
    133
    # Принимает переменное количество позиционных или именованных аргументов def spam(*args, **kwargs):
    # args – кортеж со значениями позиционных аргументов
    # kwargs – словарь с именованными аргументами
    ...
    Словарь с дополнительными именованными аргументами также можно передать другой функции, используя синтаксис **kwargs:
    def callfunc(*args, **kwargs):
    func(*args,**kwargs)
    Такой способ использования аргументов *args и **kwargs часто применяется при создании оберток для других функций. Например, функция callfunc() принимает любые комбинации аргументов и просто передает их функции func()
    Передача параметров
    и возвращаемые значения
    Параметры функции, которые передаются ей при вызове, являются обыч- ными именами, ссылающимися на входные объекты. Семантика переда- чи параметров в языке Python не имеет точного соответствия какому-либо одному способу, такому как «передача по значению» или «передача по ссылке», которые могут быть вам знакомы по другим языкам программи- рования. Например, если функции передается неизменяемое значение, это выглядит, как передача аргумента по значению. Однако при передаче из- меняемого объекта (такого как список или словарь), который модифици- руется функцией, эти изменения будут отражаться на исходном объекте.
    Например:
    a = [1, 2, 3, 4, 5]
    def square(items):
    for i,x in enumerate(items):
    items[i] = x * x # Изменяется элемент в самом списке
    ёё
    square(a) # Изменит содержимое списка: [1, 4, 9, 16, 25]
    Если функция изменяет значения аргументов или оказывает влияние на состояние других частей программы, про такие функции говорят, что они имеют побочные эффекты. В общем случае подобного стиля программиро- вания лучше избегать, потому что с ростом размеров или сложности про- граммы такие функции могут стать источником трудноуловимых ошибок
    (из инструкции вызова функции сложно определить, имеет ли она побоч- ные эффекты). Такие функции трудно приспособить для работы в составе многопоточных программ, потому что побочные эффекты обычно прихо- дится защищать блокировками.
    Инструкция return возвращает значение из функции. Если значение не указано или опущена сама инструкция return, вызывающей программе возвращается объект None. Чтобы вернуть несколько значений, достаточно поместить их в кортеж:

    134
    Глава 6. Функции и функциональное программирование def factor(a):
    d = 2
    while (d <= (a / 2)):
    if ((a / d) * d == a):
    return ((a / d), d)
    d = d + 1
    return (a, 1)
    Е
    сли функция возвращает несколько значений в виде кортежа, их можно присвоить сразу нескольким отдельным переменным:
    x, y = factor(1243) # Возвращаемые значения записываются в переменные x и y.
    или
    (x, y) = factor(1243) # Альтернативная версия. Результат тот же самый.
    Правила видимости
    При каждом вызове функции создается новое локальное пространство имен. Это пространство имен представляет локальное окружение, содер- жащее имена параметров функции, а также имена переменных, которым были присвоены значения в теле функции. Когда возникает необходимость отыскать имя, интерпретатор в первую очередь просматривает локальное пространство имен. Если искомое имя не было найдено, поиск продолжа- ется в глобальном пространстве имен. Глобальным пространством имен для функций всегда является пространство имен модуля, в котором эта функция была определена. Если интерпретатор не найдет искомое имя в глобальном пространстве имен, поиск будет продолжен во встроенном пространстве имен. Если и эта попытка окажется неудачной, будет возбуж- дено исключение NameError.
    Одна из необычных особенностей пространств имен заключается в работе с глобальными переменными внутри функции. Рассмотрим в качестве при- мера следующий фрагмент:
    a = 42
    def foo():
    a = 13
    foo()
    # Переменная a по-прежнему имеет значение 42
    После выполнения этого фрагмента переменная a по-прежнему будет иметь значение 42, несмотря на то что внутри функции foo значение переменной a изменяется. Когда внутри функции выполняется операция присваивания значения переменной, она всегда выполняется в локальном пространстве имен функции; в результате переменная a в теле функции ссылается на со- вершенно другой объект, содержащий значение 13, а не на тот, на который ссылается внешняя переменная. Обеспечить иное поведение можно с по- мощью инструкции global. Она просто объявляет принадлежность имен глобальному пространству имен; использовать ее необходимо, только когда потребуется изменить глобальную переменную. Ее можно поместить где- нибудь в теле функции и использовать неоднократно. Например:

    Правила видимости
    135
    a = 42
    b = 37
    def foo():
    global a # переменная ‘a’ находится в глобальном пространстве имен a = 13
    b = 0
    foo()
    # теперь a имеет значение 13. b – по-прежнему имеет значение 37.
    В языке Python поддерживается возможность определять вложенные функ-
    Python поддерживается возможность определять вложенные функ- поддерживается возможность определять вложенные функ- ции. Например:
    def countdown(start):
    n = start def display(): # Объявление вложенной функции print(‘T-minus %d’ % n)
    while n > 0:
    display()
    n -= 1
    Переменные во вложенных функциях привязаны к лексической области
    видимости
    . То есть поиск имени переменной начинается в локальной об- ласти видимости и затем последовательно продолжается во всех объемлю- щих областях видимости внешних функций, в направлении от внутрен- них к внешним. Если и в этих пространствах имен искомое имя не будет найдено, поиск будет продолжен в глобальном, а затем во встроенном про- странстве имен, как и прежде. Несмотря на доступность промежуточных вмещающих областей видимости, Python 2 позволяет выполнять присваи-
    Python 2 позволяет выполнять присваи-
    2 позволяет выполнять присваи- вание только переменным, находящимся в самой внутренней (локальные переменные) и в самой внешней (при использовании инструкции global) области видимости. По этой причине вложенные функции не имеют воз- можности присваивать значения локальным переменным, определенным во внешних функциях. Например, следующий программный код не будет работать:
    def countdown(start):
    n = start def display():
    print(‘T-minus %d’ % n)
    def decrement():
    n -= 1 # Не будет работать в Python 2
    while n > 0:
    display()
    decrement()
    В Python 2 эту проблему можно решить, поместив значения, которые пред-
    Python 2 эту проблему можно решить, поместив значения, которые пред-
    2 эту проблему можно решить, поместив значения, которые пред- полагается изменять, в список или в словарь. В Python 3 можно объявить переменную n как nonlocal, например:
    def countdown(start):
    n = start def display():
    print(‘T-minus %d’ % n)
    def decrement():

    136
    Глава 6. Функции и функциональное программирование nonlocal n # Связывает имя с внешней переменной n (только в Python 3)
    n -= 1
    while n > 0:
    display()
    decrement()
    Объявление nonlocal не может использоваться для привязки указанного имени к локальной переменной, определенной внутри одной из вложенных функций (то есть в динамической области видимости). Поэтому для тех, кто имеет опыт работы с языком Perl, замечу, что объявление nonlocal – это не то же самое, что объявление local в языке Perl.
    При обращении к локальной переменной до того, как ей будет присвоено значение, возбуждается исключение UnboundLocalError. Следующий пример демонстрирует один из возможных сценариев, когда такое исключение мо- жет возникнуть:
    i = 0
    def foo():
    i = i + 1 # Приведет к исключению UnboundLocalError print(i)
    В этой функции переменная i определяется как локальная (потому что внутри функции ей присваивается некоторое значение и отсутствует ин- струкция global). При этом инструкция присваивания i = i + 1 пытает- ся прочитать значение переменной i еще до того, как ей будет присвоено значение. Хотя в этом примере существует глобальная переменная i, она не используется для получения значения. Переменные в функциях могут быть либо локальными, либо глобальными и не могут произвольно изме- нять область видимости в середине функции. Например, нельзя считать, что переменная i в выражении i + 1 в предыдущем фрагменте обращается к глобальной переменной i; при этом переменная i в вызове print(i) подра- зумевает локальную переменную i, созданную в предыдущей инструкции.
    Функции как объекты и замыкания
    Функции в языке Python – объекты первого класса. Это означает, что они могут передаваться другим функциям в виде аргументов, сохраняться в структурах данных и возвращаться функциями в виде результата. Ниже приводится пример функции, которая на входе принимает другую функ- цию и вызывает ее:
    # foo.py def callf(func):
    return func()
    А это пример использования функции, объявленной выше:
    >>> import foo
    >>> def helloworld():
    ...
    return ‘Привет, Мир!’
    ...
    >>> foo.callf(helloworld) # Передача функции в виде аргумента

    Функции как объекты и замыкания
    137
    ‘Привет, Мир!’
    >>>
    Когда функция интерпретируется как данные, она неявно несет инфор- мацию об окружении, в котором была объявлена функция, что оказывает влияние на связывание свободных переменных в функции. В качестве при- мера рассмотрим модифицированную версию файла foo.py, в который были добавлены переменные:
    # foo.py x = 42
    def callf(func):
    return func()
    Теперь исследуем следующий пример:
    >>> import foo
    >>> x = 37
    >>> def helloworld():
    ...
    return “Привет, Мир! x = %d” % x
    ...
    >>> foo.callf(helloworld) # Передача функции в виде аргумента
    ‘Привет, Мир! x = 37’
    >>>
    Обратите внимание, как функция helloworld() в этом примере использует значение переменной x, которая была определена в том же окружении, что и сама функция helloworld(). Однако хотя переменная x определена в файле foo.py и именно там фактически вызывается функция helloworld(), при ис- полнении функцией helloworld() используется не это значение переменной x.
    Когда инструкции, составляющие функцию, упаковываются вместе с окру- жением, в котором они выполняются, получившийся объект называют за-
    мыканием
    . Такое поведение предыдущего примера объясняется наличием у каждой функции атрибута __globals__, ссылающегося на глобальное про- странство имен, в котором функция была определена. Это пространство имен всегда соответствует модулю, в котором была объявлена функция.
    Для предыдущего примера атрибут __globals__ содержит следующее:
    >>> helloworld.__globals
    __
    {‘__builtins__’: ,
    ‘helloworld’: ,
    ‘x’: 37, ‘__name__’: ‘__main__’, ‘__doc__’: None
    ‘foo’: }
    >>>
    Когда функция используется как вложенная, в замыкание включается все ее окружение, необходимое для работы внутренней функции. Например:
    import foo def bar():
    x = 13
    def helloworld():
    return “Привет, Мир! x = %d” % x foo.callf(helloworld) # вернет ‘Привет, Мир! x = 13’

    138
    Глава 6. Функции и функциональное программирование
    Замыкания и вложенные функции особенно удобны, когда требуется на- писать программный код, реализующий концепцию отложенных вычис- лений. Рассмотрим еще один пример:
    from urllib import urlopen
    # from urllib.request import urlopen (Python 3)
    def page(url):
    def get():
    return urlopen(url).read()
    return get
    Функция page() в этом примере не выполняет никаких вычислений. Она просто создает и возвращает функцию get(), которая при вызове будет из- влекать содержимое веб-страницы. То есть вычисления, которые произ- водятся в функции get(), в действительности откладываются до момента, когда фактически будет вызвана функция get(). Например:
    >>> python = page(“http://www.python.org”)
    >>> jython = page(“http://www.jython.org”)
    >>> python

    >>> jython

    >>> pydata = python() # Извлечет страницу http://www.python.org
    >>> jydata = jython() # Извлечет страницу http://www.jython.org
    >>>
    Две переменные, python и jython, объявленные в этом примере, в действи- тельности являются двумя различными версиями функции get(). Хотя функция page(), которая создала эти значения, больше не выполняется, тем не менее обе версии функции get() неявно несут в себе значения внешних переменных на момент создания функции get(). То есть при выполнении функция get() вызовет urlopen(url) со значением url, которое было передано функции page(). Взглянув на атрибуты объектов python и jython, можно уви- деть, какие значения переменных были включены в замыкания. Например:
    >>>
    1   ...   7   8   9   10   11   12   13   14   ...   82


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