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

  • 17.3. Множественные параметры

  • 17.4. Параметры по умолчанию

  • 17.5. Правила выбора имен для функций

  • Рис. 18.1.

  • 18.3. Приращения в срезах

  • Как устроен Python. Как устроен Python. Харрисон. Харрисон Мэтт


    Скачать 5.41 Mb.
    НазваниеХаррисон Мэтт
    АнкорКак устроен Python
    Дата05.02.2022
    Размер5.41 Mb.
    Формат файлаpdf
    Имя файлаКак устроен Python. Харрисон.pdf
    ТипДокументы
    #352210
    страница13 из 21
    1   ...   9   10   11   12   13   14   15   16   ...   21
    17.1. Вызов функций
    В Python функции вызываются по имени функции, за которым следуют круглые скобки. В следующем фрагменте вызывается только что опре- деленная функция add_2
    :
    >>> add_2(3)
    5
    Чтобы вызвать функцию, укажите ее имя, за которым следует открываю- щая круглая скобка, входные параметры и закрывающая круглая скобка.

    156
    Глава 17. Функции
    Количество параметров должно соответствовать количеству параметров в объявлении функции. Обратите внимание: REPL выводит результат вызова — целое число 5 (то, что возвращает команда return
    ).
    Функции add_2
    можно передать произвольный объект. Но если этот объект не поддерживает сложение с числами, будет выдано исключение.
    При передаче строки выдается исключение
    TypeError
    :
    >>> add_2('hello')
    Traceback (most recent call last):
    TypeError: must be str, not int
    17.2. Область видимости
    Python ищет переменные в разных местах. Эти места называются обла-
    стями видимости или пространствами имен. При поиске переменной (не забывайте, что функции в Python также являются переменными — как и классы, модули и т. д.), Python выполняет поиск в следующих местах и в следующем порядке:
    
    Локальная область видимости — переменные, определенные внутри функций.
    
    Глобальная область видимости — переменные, определяемые на гло- бальном уровне.
    
    Встроенная область видимости — переменные, заранее определен- ные в Python.
    В следующем коде поиск переменных осуществляется по всем трем об- ластям видимости:
    >>> x = 2 # Глобальная
    >>> def scope_demo():
    ... y = 4 # Локальная для scope_demo
    ... print("Local: {}".format(y))
    ... print("Global: {}".format(x))
    ... print("Built-in: {}".format(dir))
    >>> scope_demo()
    Local: 4
    Global: 2
    Built-in:

    17.2. Область видимости
    157
    После вызова scope_demo локальная переменная y
    уничтожается в ходе уборки мусора и становится недоступной в глобальной области види- мости:
    >>> y
    Traceback (most recent call last):
    NameError: name 'y' is not defined
    Переменные, определяемые внутри функции или метода, являются локальными. В общем случае стоит избегать глобальных переменных, потому что они усложняют понимание кода. Глобальные переменные часто встречаются в учебниках, блогах и документации, потому что их использование сокращает объем кода и помогает сосредоточиться на кон- цепциях, не отвлекаясь на упаковку переменных в функциях. Функции и классы помогают избавиться от глобальных переменных, улучшают модульность кода и упрощают его понимание.
    ПРИМЕЧАНИЕ
    Python позволяет замещать (переопределять) переменные в глобальной и встроенной области видимости. На глобальном уровне вы можете опре- делить собственную переменную с именем dir
    . В этот момент встроенная функция dir
    замещается глобальной переменной. То же самое можно сде- лать внутри функции и создать локальную переменную, которая замещает глобальную или встроенную переменную:
    >>> def dir(x):
    ... print("Dir called")
    >>> dir('')
    Dir called
    Команда del может использоваться для удаления переменных в локальной или глобальной области видимости. Однако на практике лучше с самого на- чала избегать замещения встроенных имен:
    >>> del dir
    >>> dir('')
    ['__add__', '__class__', '__contains__', ... ]

    158
    Глава 17. Функции
    ПОДСКАЗКА
    Функции locals и globals используются для вывода содержимого этих областей видимости. Они возвращают словари с текущим содержимым об- ласти видимости:
    >>> def foo():
    ... x = 1
    ... print(locals())
    >>> foo()
    {'x': 1}
    Переменная
    __builtins__
    выводит имена из встроенной области види- мости. Ее атрибут
    __dict__
    выдает такой же словарь, как для глобальных и локальных имен.
    17.3. Множественные параметры
    Функции могут получать несколько параметров. Следующая функция получает два параметра и возвращает их сумму:
    >>> def add_two_nums(a, b):
    ... return a + b
    Так как Python является динамическим языком, указывать типы пара- метров не нужно. Эта функция может суммировать два целых числа:
    >>> add_two_nums(4, 6)
    10
    А может суммировать числа с плавающей точкой:
    >>> add_two_nums(4.0, 6.0)
    10.0
    И строки тоже:
    >>> add_two_nums('4', '6')
    '46'
    Обратите внимание: для строк используется операция
    +
    для выполнения
    конкатенации (сцепления двух строк).

    17.4. Параметры по умолчанию
    159
    Но если вы попробуете сложить строку с числом, Python сообщит об ошибке:
    >>> add_two_nums('4', 6)
    Traceback (most recent call last):
    TypeError: Can't convert 'int' object to str implicitly
    Это одна из тех ситуаций, когда Python требует более точно описать нужную операцию и не пытается угадывать за вас. Если вы хотите сло- жить строковый тип с числом, возможно, сначала нужно преобразовать их к числовому формату (при помощи float или int
    ). С другой стороны, если нужно выполнить операцию конкатенации, следует преобразовать числа в строки. Python не выбирает выполняемую операцию автомати- чески. Вместо этого выдается ошибка, которая заставляет программиста разрешить неоднозначность.
    17.4. Параметры по умолчанию
    Одна из удобных особенностей функций Python — параметры по
    умолчанию. Как следует из названия, они позволяют задать значения по умолчанию для параметров функций. Параметры по умолчанию не являются обязательными, хотя при необходимости их можно пере- определить.
    Следующая функция похожа на add_two_nums
    , но если при вызове второе число не указано, по умолчанию прибавляется 3:
    >>> def add_n(num, n=3):
    ... """default to
    ... adding 3"""
    ... return num + n
    >>> add_n(2)
    5
    >>> add_n(15, -5)
    10
    Чтобы создать для параметра значение по умолчанию, поставьте после параметра знак равенства (
    =
    ) и нужное значение.

    160
    Глава 17. Функции
    ПРИМЕЧАНИЕ
    Параметры по умолчанию должны объявляться после обычных параметров, в противном случае Python выдаст ошибку
    SyntaxError
    :
    >>> def add_n(num=3, n):
    ... return num + n
    Traceback (most recent call last):
    SyntaxError: non-default argument follows default argument
    Python требует, чтобы обязательные параметры были объявлены ранее не- обязательных. Приведенный выше код не будет работать для вызова вида add_n(4)
    , потому что отсутствует обязательный параметр.
    СОВЕТ
    Не используйте изменяемые типы (списки, словари) в качестве параметров по умолчанию — разве что вы очень хорошо понимаете, что делаете. Из-за особенностей работы Python параметры по умолчанию создаются только один раз — во время определения функции, а не во время ее выполнения. Если вы используете изменяемое значение по умолчанию, то при каждом вызове функ- ции будет заново использован тот же экземпляр параметра по умолчанию:
    >>> def to_list(value, default=[]):
    ... default.append(value)
    ... return default
    >>> to_list(4)
    [4]
    >>> to_list('hello')
    [4, 'hello']
    Тот факт, что параметры по умолчанию создаются в момент генерирования функции, многие программисты считают дефектом. Это связано с тем, что такое поведение чревато разными неожиданностями. Обходное решение заключается в том, чтобы вынести создание значений по умолчанию из фазы определения функции (которая выполняется всего один раз) в фазу выполнения функции
    (чтобы новое значение создавалось при каждом выполнении функции).
    Модифицируйте изменяемые параметры по умолчанию, чтобы им присваи- валось значение
    None
    . Затем создайте экземпляр нужного изменяемого типа в теле функции, если значение по умолчанию равно
    None
    :

    17.5. Правила выбора имен для функций
    161
    >>> def to_list2(value, default=None):
    ... if default is None:
    ... default = []
    ... default.append(value)
    ... return default
    >>> to_list2(4)
    [4]
    >>> to_list2('hello')
    ['hello']
    Следующий код:
    ... if default is None:
    ... default = []
    можно записать в одну строку с использованием условного выражения:
    ... default = default if default is not None else []
    17.5. Правила выбора имен для функций
    Правила выбора имен функций имеют много общего с правилами выбора имен переменных (и они также находятся в документе PEP 8). В именах используется так называемый змеиный регистр, который проще читается.
    Имена функций:
    
    должны записываться в нижнем регистре;
    
    слова_должны_разделяться_подчеркиваниями;
    
    не должны начинаться с цифр;
    
    не должны переопределять встроенные имена;
    
    не должны совпадать с ключевыми словами.
    В таких языках, как Java, используется так называемый «верблюжий регистр». По этой схеме создаются имена переменных вида sectionList или hasTimeOverlap
    . В Python переменным были бы присвоены имена section_list и has_time_overlap соответственно. Хотя код Python должен следовать соглашениям PEP 8, в PEP 8 также принимается в расчет един- ство стиля. Если в коде, над котором вы работаете, используются разные

    162
    Глава 17. Функции схемы назначения имен, следуйте примеру и используйте схему, применя- емую в существующем коде. Собственно, в модуле unittest из стандартной библиотеки до сих пор применяется схема в стиле Java (потому что изна- чально этот модуль был импортирован из библиотеки Java junit
    ).
    17.6. Итоги
    Функции позволяют инкапсулировать изменения и побочные эффекты в своем теле. В этой главе вы узнали, что функции могут получать ввод и возвращать результат. Входных параметров может быть несколько, и им можно назначать значения по умолчанию.
    Вспомните, что в Python нет ничего, кроме объектов, а при создании функции вы также создаете переменную с именем функции, которая указывает на эту функцию.
    Функции также могут включать строку документации, которая записы- вается непосредственно после объявления. Эти строки образуют доку- ментацию, которая выводится при вызове help для функции.
    17.7. Упражнения
    1. Напишите функцию is_odd
    , которая получает целое число и воз- вращает
    True для нечетных чисел или
    False для четных.
    2. Напишите функцию is_prime
    , которая получает целое число и воз- вращает
    True для простых чисел или
    False для чисел, не являющих- ся простыми.
    3. Напишите функцию бинарного поиска. Функция должна получать отсортированную последовательность и искомый элемент и воз- вращать индекс найденного элемента. Если элемент не найден, функция должна возвращать –1.
    4. Напишите функцию, которая получает строки в «верблюжьем ре- гистре» (
    ThisIsCamelCased
    ) и преобразует их в «змеиный регистр»
    (
    this_is_camel_cased
    ). Измените функцию, добавив в нее аргумент separator
    , чтобы функция также могла выполнять преобразование к «кебаб-регистру» (
    this-is-camel-case
    ).

    18
    Индексирование и срезы
    Python предоставляет две конструкции для извлечения данных из после- довательностей (списки, кортежи и даже строки). Речь идет о конструк- циях индексирования и срезах. Индексирование позволяет извлекать отдельные элементы из последовательности, а срезы предназначены для извлечения подпоследовательностей.
    18.1. Индексирование
    Индексирование уже было продемонстрировано ранее для списков. На- пример, если у вас имеется список с названиями животных, вы сможете выбирать элементы по индексу:
    >>> my_pets = ["dog", "cat", "bird"]
    >>> my_pets[0]
    'dog'
    СОВЕТ
    Напомним, что в Python индексирование начинается с 0. Чтобы извлечь первый элемент, используйте индекс 0, а не 1.
    В Python предусмотрена удобная возможность обращения к элементам по отрицательным индексам. Индекс –1 обозначает последний элемент,
    –2 — предпоследний и т. д. Эта запись чаще всего используется для полу- чения последнего элемента списка:
    >>> my_pets[-1]
    'bird'

    164
    Глава 18. Индексирование и срезы
    Гвидо ван Россум, создатель Python, в своем твите объяснил, как следует понимать отрицательные значения индексов:
    «…Правильный подход [к отрицательному индексированию] — ин- терпретировать a[-X]
    как a[len(a)-X]
    »
    @gvanrossum
    Операции индексирования также можно выполнять с кортежами и стро- ками:
    >>> ('Fred', 23, 'Senior')[1]
    23
    >>> 'Fred'[0]
    'F'
    Некоторые типы, например множества, не поддерживают операции индексирования. Если вы хотите определить собственный класс, под- держивающий операции индексирования, реализуйте метод
    .__getitem__
    -3
    Примеры индексов
    Последовательность
    Индекс
    0 1 2 3 4 5 6 7
    d a t a . c s v
    "
    "
    -8-7-6-5-4
    -2
    -3
    -1
    Отрицательный индекс data.csv[0]
    data.csv[-3]
    Рис. 18.1. Положительные и отрицательные значения индексов
    18.2. Срезы
    Кроме извлечения одного элемента по целочисленному индексу вы можете воспользоваться срезом (slice) для извлечения подпоследова- тельности. Срез может содержать начальный индекс, необязательный

    18.2. Срезы
    165
    конечный индекс и необязательное приращение (все значения разделя- ются двоеточиями).
    Срез для извлечения первых двух элементов списка:
    >>> my_pets = ["dog", "cat", "bird"] # список
    >>> print(my_pets[0:2])
    ['dog', 'cat']
    Напомним, что в Python используются полуоткрытые интервалы. Спи- сок доходит до конечного индекса, но не включает его. Как упоминалось ранее, функция range также аналогично ведет себя со вторым параметром.
    Примеры срезов
    Последовательность
    Индекс
    0 1 2 3 4 5 6 7
    d a t a . c s v
    "
    "
    -8-7-6-5-4-3-2-1
    Отрицательный индекс "data.csv"[0:4]
    "data.csv"[5:8]
    "data.csv"[:4]
    "data.csv"[-8:-4]
    "data"
    "data.csv"[5:]
    "data.csv"[-3:]
    "csv"
    Рис. 18.2. Выделение первых четырех символов строки. Показаны три варианта; предпочтительным считается последний. Нулевой индекс не указан, так как он используется по умолчанию. Решение с отрицательными индексами выглядит просто глупо. Также продемонстрирован срез трех последних символов. И снова последний вариант считается идиоматическим решением. Первые два варианта предполагают, что длина строки равна 8 символам, а последний будет работать с любой строкой, содержащей не менее 3 символов
    При определении среза с двоеточием (
    :
    ) первый индекс не является обязательным. Если первый индекс не указан, то срез по умолчанию на- чинается с первого элемента списка (нулевой элемент):

    166
    Глава 18. Индексирование и срезы
    >>> print(my_pets[:2])
    ['dog', 'cat']
    В срезах также могут использоваться отрицательные индексы. Отри- цательной может быть как начальная, так и конечная позиция. Индекс
    –1 представляет последний элемент. Если срез распространяется до по- следнего элемента, вы получите все, кроме этого элемента:
    >>> my_pets[0:-1]
    ['dog', 'cat']
    >>> my_pets[:-1] # defaults to 0
    ['dog', 'cat']
    >>> my_pets[0:-2]
    ['dog']
    Последний индекс также не является обязательным. Если последний ин- декс отсутствует, срез по умолчанию распространяется до конца списка:
    >>> my_pets[1:]
    ['cat', 'bird']
    >>> my_pets[-2:]
    ['cat', 'bird']
    Наконец, для начального и конечного индекса могут использоваться значения по умолчанию. Если оба индекса отсутствуют, то возвращае- мый срез проходит от начала до конца (и содержит копию списка). Эта конструкция может использоваться для быстрого копирования списков в Python:
    >>> print(my_pets[:])
    ['dog', 'cat', 'bird']
    18.3. Приращения в срезах
    После начального и конечного индекса срез также может получать при- ращение. Если приращение не задано, по умолчанию используется зна- чение 1. Приращение 1 означает, что из последовательности извлекается каждый элемент между индексами. С приращением 2 берется каждый второй элемент, с приращением 3 — каждый третий и т. д.:

    18.3. Приращения в срезах
    167
    >>> my_pets = ["dog", "cat", "bird"]
    >>> dog_and_bird = my_pets[0:3:2]
    >>> print(dog_and_bird)
    ['dog', 'bird']
    >>> zero_three_six = [0, 1, 2, 3, 4, 5, 6][::3]
    >>> print(zero_three_six)
    [0, 3, 6]
    ПРИМЕЧАНИЕ
    Функция range также поддерживает третий параметр, задающий прираще- ние:
    >>> list(range(0, 7, 3))
    [0, 3, 6]
    Приращение может быть отрицательным. Приращение –1 означает, что вы двигаетесь в обратном направлении справа налево. Чтобы использо- вать отрицательное приращение, укажите значение начального индекса больше конечного. Исключением является ситуация, в которой опущен как начальный, так и конечный индекс: приращение –1 переставляет элементы последовательности в обратном порядке:
    >>> my_pets[0:2:-1]
    []
    >>> my_pets[2:0:-1]
    ['bird', 'cat']
    >>> print([1, 2, 3, 4][::-1])
    [4, 3, 2, 1]
    Когда в следующий раз на собеседовании вам предложат переставить символы строки в обратном порядке, это можно сделать в одной строке:
    >>> 'emerih'[::-1]
    'hireme'
    Конечно, от вас, скорее всего, потребуют сделать это на C. Просто ска- жите, что вы хотите программировать на Python!

    168
    Глава 18. Индексирование и срезы
    18.4. Итоги
    Операции индексирования используются для извлечения отдельных значений из последовательностей. Например, они позволяют легко полу- чить символ из строки или элемент из списка либо кортежа.
    Если вам нужна подпоследовательность, используйте синтаксическую конструкцию среза. Срезы соблюдают принцип полуоткрытых интерва- лов и дают последовательность до конечного индекса (не включая его).
    Если вы передадите необязательное приращение, то сможете пропускать элементы при формировании среза.
    В Python предусмотрена удобная возможность использования отри- цательных значений для индексирования или создания среза относи- тельно конца последовательности. Это позволяет выполнять операции относительно длины последовательности, так что вам не приходится беспокоиться о вычислении длины и вычитании смещений из получен- ного результата.
    18.5. Упражнения
    1. Создайте переменную с вашим именем, хранящимся в формате стро- ки. Используйте операции индексирования для получения первого символа. Извлеките последний символ. Будет ли ваш код для извле- чения последнего символа работать с именем произвольной длины?
    2. Создайте переменную filename
    . Предполагая, что за именем файла следует трехбуквенное расширение, найдите расширение с исполь- зованием операции среза. Так, для файла
    README.txt должно быть получено расширение txt
    . Будет ли ваш код работать с именами файлов произвольной длины?
    3. Создайте функцию is_palindrome для проверки того, что передан- ное слово одинаково читается в обоих направлениях.

    1   ...   9   10   11   12   13   14   15   16   ...   21


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