ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 03.12.2023
Просмотров: 398
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Поверхностное копирование списков
125
Но с выходом Python 3.6 f-строки (сокращение от format strings) предоставляют более удобный способ создания строк, включающих другие строки. Подобно тому как у необработанных строк перед первой кавычкой ставится префикс r
, f-строки помечаются префиксом f
. В f-строки можно включать имена переменных в фигур- ных скобках, чтобы вставлять строки, хранящиеся в этих переменных:
>>> name, day, weather = 'Al', 'Sunday', 'sunny'
>>> f'Hello, {name}. Today is {day} and it is {weather}.'
'Hello, Al. Today is Sunday and it is sunny.'
Фигурные скобки могут содержать целые выражения:
>>> width, length = 10, 12
>>> f'A {width} by {length} room has an area of {width * length}.'
'A 10 by 12 room has an area of 120.'
Если в f-строку нужно включить фигурную скобку как литерал, экранируйте ее дополнительной фигурной скобкой:
>>> spam = 42
>>> f'This prints the value in spam: {spam}'
'This prints the value in spam: 42'
>>> f'This prints literal curly braces: {{spam}}'
'This prints literal curly braces: {spam}'
Так как имена переменных и выражений можно встраивать прямо в строку, код читается лучше, чем со старыми средствами форматирования строк.
Все эти разные способы форматирования противоречат тезису из свода правил
«Дзен Python», согласно которому должно существовать одно — и желательно только одно — очевидное решение. Но f-строки являются усовершенствованием языка (по моему мнению), а, как указано в других рекомендациях, практичность важнее безупречности. Если вы пишете код только для Python 3.6 и выше, исполь- зуйте f-строки. Если ваш код может выполняться в более ранних версиях Python, придерживайтесь метода format()
или спецификаторов преобразования
%s
Поверхностное копирование списков
Синтаксис сегментов позволяет легко создавать новые строки или списки на базе уже существующих. Чтобы увидеть, как это делается, введите следующие команды в интерактивной оболочке:
>>> 'Hello, world!'[7:12] # Создание строки из большей строки.
'world'
>>> 'Hello, world!'[:5] # Создание строки из большей строки.
'Hello'
126
Глава 6.Написание питонического кода
>>> ['cat', 'dog', 'rat', 'eel'][2:] # Создание списка из большего списка.
['rat', 'eel']
Двоеточие (
:
) разделяет начальный и конечный индексы элементов, помещаемые в создаваемый список. Если начальный индекс перед двоеточием не указан (как в 'Hello,
world!'[:5]
), то начальный индекс по умолчанию равен 0. Если опустить конечный индекс после двоеточия, как в
['cat',
'dog',
'rat',
'eel'][2:]
, то ко- нечным индексом по умолчанию становится конец списка.
Если опущены оба индекса, то начальный индекс равен 0 (начало списка), а ко- нечный индекс соответствует концу списка. Фактически эта конструкция создает копию списка:
>>> spam = ['cat', 'dog', 'rat', 'eel']
>>> eggs = spam[:]
>>> eggs
['cat', 'dog', 'rat', 'eel']
>>> id(spam) == id(eggs)
False
Обратите внимание: списки spam и eggs не тождественны. Строка eggs
=
spam[:]
создает поверхностную копию списка в spam
, тогда как eggs
=
spam копирует только ссылку на список. Но синтаксис
[:]
выглядит немного странно, а создание по- верхностной копии списка функцией copy()
модуля copy читается значительно лучше:
>>> # Пример питонического кода.
>>> import copy
>>> spam = ['cat', 'dog', 'rat', 'eel']
>>> eggs = copy.copy(spam)
>>> id(spam) == id(eggs)
False
Вы должны знать об этом странном синтаксисе на случай, если вам попадется код
Python, в котором он используется, но я не рекомендую применять его в своем коде. Помните, что как
[:]
, так и вызов copy.copy()
создают поверхностные копии.
Питонические способы использования словарей
Словари играют важную роль во многих программах Python из-за гибкости пар
«ключ — значение» (см. главу 7), связывающих один вид данных с другим. А значит, вам пригодятся некоторые словарные идиомы, часто используемые в коде Python.
За дополнительной информацией о словарях обращайтесь к великолепным до- кладам программиста Python Брэндона Родса (Brandon Rhodes), посвященным словарям и тому, как они работают: «The Mighty Dictionary» на конференции
Питонические способы использования словарей
127
PyCon 2010 (https://invpy.com/mightydictionary) и «The Dictionary Even Mightier» на конференции PyCon 2017 (https://invpy.com/dictionaryevenmightier).
Использование get() и setdefault() со словарями
Попытка обратиться к несуществующему ключу словаря приводит к ошибке
KeyError
, поэтому для предотвращения ошибки программисты часто пишут не- питонический код:
>>> # Пример непитонического кода
>>> numberOfPets = {'dogs': 2}
>>> if 'cats' in numberOfPets: # Проверить, существует ли ключ 'cats'.
... print('I have', numberOfPets['cats'], 'cats.')
... else:
... print('I have 0 cats.')
I have 0 cats.
Этот код проверяет, существует ли строка 'cats'
как ключ в словаре numberOfPets
Если ключ существует, то вызов print()
обращается к numberOfPets['cats']
как части сообщения для пользователя. Если ключ не существует, то другой вызов print()
выводит строку без обращения к numberOfPets['cats']
, поэтому исклю- чение
KeyError не выдается.
Данная схема встречается настолько часто, что у словарей имеется метод get()
, который позволяет задать значение по умолчанию, возвращаемое в случае, если ключ не существует в словаре. Следующий питонический код эквивалентен пре- дыдущему примеру:
>>> # Пример питонического кода.
>>> numberOfPets = {'dogs': 2}
>>> print('I have', numberOfPets.get('cats', 0), 'cats.')
I have 0 cats.
Вызов numberOfPets.get('cats',
0)
проверяет, существует ли ключ 'cats'
в сло- варе numberOfPets
. Если он существует, то вызов метода возвращает значение для ключа 'cats'
. Если ключ не существует, вместо значения возвращается второй аргумент 0. Использование метода get()
с определением значения по умолчанию, которое должно использоваться для несуществующих ключей, короче и лучше читается, чем решение с командами if
- else
И наоборот, может потребоваться задать значение по умолчанию, если ключ не существует. Например, если словарь из numberOfPets не содержит ключа 'cats'
, команда numberOfPets['cats']
+=
10
приводит к ошибке
KeyError
. Можно доба- вить код, который проверяет возможное отсутствие ключа и задает значение по умолчанию:
128
Глава 6.Написание питонического кода
>>> # Пример непитонического кода
>>> numberOfPets = {'dogs': 2}
>>> if 'cats' not in numberOfPets:
... numberOfPets['cats'] = 0
>>> numberOfPets['cats'] += 10
>>> numberOfPets['cats']
10
Но этот паттерн встречается настолько часто, что у словарей имеется более пито- нический метод setdefault()
. Следующий код эквивалентен предыдущему:
>>> # Пример питонического кода.
>>> numberOfPets = {'dogs': 2}
>>> numberOfPets.setdefault('cats', 0) # Ничего не делать, если 'cats' существует.
0
>>> workDetails['cats'] += 10
>>> workDetails['cats']
10
Если вы пишете команды if
, которые проверяют, существует ли код в словаре, и за- дают значение по умолчанию при его отсутствии, используйте метод setdefault()
Использование collections.defaultdict для значений по умолчанию
Класс collections.defaultdict можно использовать для полного устранения ошибок
KeyError
. Этот класс позволяет создать словарь по умолчанию; для этого импортируйте модуль collections и вызовите метод collections.defaultdict()
, передав ему тип данных, который должен использоваться для значения по умолча- нию. Например, передавая int методу collections.defaultdict()
, можно создать объект, похожий на словарь, в котором
0
используется как значение по умолчанию для несуществующих ключей. Введите следующий фрагмент в интерактивной оболочке:
>>> import collections
>>> scores = collections.defaultdict(int)
>>> scores
defaultdict(
>>> scores['Al'] += 1 # Не нужно сначала задавать значение для ключа 'Al'.
>>> scores
defaultdict(
>>> scores['Zophie'] # Не нужно сначала задавать значение для ключа 'Zophie'.
0
>>> scores['Zophie'] += 40
>>> scores
defaultdict(
Питонические способы использования словарей
129
Обратите внимание: вы передаете функцию int()
, а не вызываете ее, что позволяет опустить круглые скобки после int в collections.defaultdict(int)
. Также можно передать список, который будет использоваться как пустой список, в значении по умолчанию. Введите следующий фрагмент в интерактивной оболочке:
>>> import collections
>>> booksReadBy = collections.defaultdict(list)
>>> booksReadBy['Al'].append('Oryx and Crake')
>>> booksReadBy['Al'].append('American Gods')
>>> len(booksReadBy['Al'])
2
>>> len(booksReadBy['Zophie']) # Значение по умолчанию - пустой список.
0
Если вам нужно значение по умолчанию для каждого возможного ключа, исполь- зовать collections.defaultdict()
намного проще, чем использовать обычный словарь и многократно вызывать метод setdefault()
Использование словарей вместо команды switch
В таких языках, как Java, существует команда switch
— разновидность команды if-elif
- else
, выполняющей код в зависимости от того, какое из многих значений содержит конкретная переменная. В Python нет команды switch
, поэтому програм- мисты Python иногда пишут такой код, как в следующем примере. Он выполняет разные команды присваивания в зависимости от того, какое из многих значений содержит переменная season
:
# Все следующие условия if и elif содержат "season ==":
if season == 'Winter':
holiday = 'New Year\'s Day'
elif season == 'Spring':
holiday = 'May Day'
elif season == 'Summer':
holiday = 'Juneteenth'
elif season == 'Fall':
holiday = 'Halloween'
else:
holiday = 'Personal day off'
Этот код не обязательно является непитоническим, но он получается слишком длинным. По умолчанию команды switch в Java работают по принципу «сквозного прохождения», из-за которого каждый блок должен завершаться командой break
В противном случае выполнение продолжается в следующем блоке. Забытые коман- ды break часто становятся источником ошибок. Но обилие команд if
- elif в нашем примере выглядит однообразно. Некоторые программисты Python предпочитают
130
Глава 6.Написание питонического кода создать словарь, вместо того чтобы использовать команды if
- elif
. Следующий компактный и питонический код эквивалентен следующему примеру:
holiday = {'Winter': 'New Year\'s Day',
'Spring': 'May Day',
'Summer': 'Juneteenth',
'Fall': 'Halloween'}.get(season, 'Personal day off')
Этот код является одной командой присваивания. В holiday сохраняется возвра- щаемое значение вызова метода get()
, который возвращает значение для ключа, присвоенного season
. Если ключ season не существует, то get()
возвращает строку 'Personal day off'
. Использование словаря делает код более компактным, но также усложняет чтение кода. Решайте сами, хотите вы использовать этот паттерн или нет.
1 ... 8 9 10 11 12 13 14 15 ... 40
Условные выражения: «некрасивый» тернарный
оператор Python
Тернарные операторы (в Python их официально называют условными выражени-
ями, или тернарными выражениями выбора) вычисляют выражение и выбирают в качестве результата одно из двух значений в зависимости от условия. Обычно это делается следующей питонической командой if
- else
:
>>> # Пример питонического кода.
>>> condition = True
>>> if condition:
... message = 'Access granted'
... else:
... message = 'Access denied'
>>> message
'Access granted'
Тернарный означает, что оператор получает три входных значения, но в програм- мировании оно синонимично условному выражению. Условные выражения также предлагают более компактное однострочное решение, соответствующее этому паттерну. В Python они реализуются странным расположением ключевых слов if и else
:
>>> valueIfTrue = 'Access granted'
>>> valueIfFalse = 'Access denied'
>>> condition = True
>>> message = valueIfTrue if condition else valueIfFalse
❶
>>> message
'Access granted'
>>> print(valueIfTrue if condition else valueIfFalse)
❷
'Access granted'
>>> condition = False
Условные выражения: «некрасивый» тернарный оператор Python
131
>>> message = valueIfTrue if condition else valueIfFalse
>>> message
'Access denied'
Выражение valueIfTrue if condition else valueIfFalse
❶
дает результат valueIfTrue
, если переменная condition равна
True
. Если переменная condition содержит
False
, то выражение дает результат valueIfFalse
. Гвидо ван Россум шутливо описывал спроектированный им синтаксис как «намеренно уродливый». Во многих языках с тернарным оператором сначала указывается условие, затем значение для
True и потом значение для
False
. Условное выражение может использоваться везде, где может применяться выражение или значение, включая аргумент вызова функции
❷
Почему этот синтаксис был включен в Python 2.5, хотя он и нарушает первую реко- мендацию «красивое лучше, чему уродливое»? К сожалению, многие программисты используют тернарные операторы, несмотря на их неудобочитаемость, и хотят, чтобы этот синтаксис поддерживался в Python. Злоупотребление ускоренным вычислением логических операторов позволяет создать некую разновидность тернарного операто- ра. Выражение condition and valueIfTrue or valueIfFalse дает результат valueIfTrue
, если condition содержит
True
, и valueIfFalse
, если condition содержит
False
(кроме одного важного случая). Введите следующий фрагмент в интерактивной оболочке:
>>> # Пример непитонического кода.
>>> valueIfTrue = 'Access granted'
>>> valueIfFalse = 'Access denied'
>>> condition = True
>>> condition and valueIfTrue or valueIfFalse
'Access granted'
У этого «псевдотернарного» оператора condition and valueIfTrue or valueIfFalse есть один неочевидный дефект: если valueIfTrue содержит квазиложное значение
(например,
0
,
False
,
None или пустая строка), выражение неожиданно дает результат valueIfFalse
, хотя condition содержит
True
Но программисты продолжают использовать этот фиктивный тернарный опера- тор, а вопрос «почему в Python нет тернарного оператора?» постоянно приходится слышать разработчикам реализации Python. Условные выражения были созданы для того, чтобы программисты перестали требовать тернарный оператор и не ис- пользовали псевдотернарный оператор с его скрытыми ошибками. Но условные выражения достаточно уродливы, чтобы отвадить программистов от их примене- ния. И хотя «красивое лучше, чем уродливое», «уродливый» тернарный оператор
Python можно считать примером того, когда практичность важнее безупречности.
Условные выражения вряд ли можно назвать питоническими, но и однозначно считать их непитоническими тоже нельзя. Если вы пользуетесь ими, избегайте вложения условных выражений в другие условные выражения:
132
Глава 6.Написание питонического кода
>>> # Пример непитонического кода.
>>> age = 30
>>> ageRange = 'child' if age < 13 else 'teenager' if age >= 13 and age < 18
else 'adult'
>>> ageRange
'adult'
Вложенные условные выражения — хороший пример того, что плотная одностроч- ная конструкция может быть технически правильной, но плохо восприниматься при чтении.
Работа со значениями переменных
При написании кода часто возникает необходимость в проверке и изменении значений, хранящихся в переменных. В Python это можно сделать несколькими способами. Рассмотрим пару примеров.
Сцепление операторов присваивания и сравнения
Когда требуется проверить, принадлежит ли число некоторому диапазону, можно воспользоваться логическим оператором and в следующей конструкции:
# Пример непитонического кода.
if 42 < spam and spam < 99:
Но Python позволяет формировать цепочки операторов сравнения, чтобы вам не приходилось использовать оператор and
. Следующий код эквивалентен предыду- щему примеру:
# Пример непитонического кода.
if 42 < spam < 99:
В цепочки можно объединять и операторы присваивания. Например, одно значение можно присвоить нескольким переменным в одной строке кода:
>>> # Пример питонического кода.
>>> spam = eggs = bacon = 'string'
>>> print(spam, eggs, bacon)
string string string
Чтобы удостовериться, что все три переменные содержат одинаковые значения, можно воспользоваться оператором and или создать цепочку из операторов
==
для проверки равенства:
>>> # Пример питонического кода.
>>> spam = eggs = bacon = 'string'
Итоги
133
>>> spam == eggs == bacon == 'string'
True
Сцепление операторов — небольшая, но полезная форма сокращенной записи в Python. Но при неправильном использовании операторы могут создать проблемы.
В главе 8 демонстрируются некоторые примеры, когда неправильное применение операторов может создать неожиданные ошибки в вашем коде.
Проверка того, что переменная содержит одно из нескольких значений
Иногда возникает ситуация, обратная описанной в предыдущем разделе: требуется проверить, содержит ли переменная одно из нескольких возможных значений. Для этого можно воспользоваться оператором or
(как в выражении spam
==
'cat'
or spam
==
'dog'
or spam
==
'moose'
). Со всеми избыточными частями spam
==
выражение становится излишне громоздким.
Вместо этого можно объединить несколько значений в кортеж и проверить, со- держится ли значение переменной в кортеже, оператором in
, как в следующем примере:
>>> # Пример питонического кода.
>>> spam = 'cat'
>>> spam in ('cat', 'dog', 'moose')
True
Этот код не только более понятен, но и по данным timeit работает чуть быстрее.
Итоги
Во всех языках программирования существуют собственные приемы и «передо- вые» практики. В этой главе я рассказал о конкретных способах написания пито- нического кода, которые создали программисты для оптимального использования синтаксиса Python.
В основе питонического кода лежат 20 тезисов документа «Дзен Python», которые отражают принципы написания кода на Python. Эти положения следует рассма- тривать как субъективное мнение; они не являются абсолютно необходимыми для написания программ на Python, но помнить о них безусловно стоит.
Значимые отступы Python (не путайте со значимыми пробелами!) вызывают больше всего протестов со стороны начинающих программистов. И хотя почти во всех языках программирования отступы используются для удобочитаемости кода,
Python требует, чтобы отступы заменяли более привычные фигурные скобки, при- меняемые в других языках.