Главная страница

Чистыйкод дляпродолжающи х


Скачать 7.85 Mb.
НазваниеЧистыйкод дляпродолжающи х
Дата13.05.2023
Размер7.85 Mb.
Формат файлаpdf
Имя файлаPython_Chisty_kod_dlya_prodolzhayuschikh_2022_El_Sveygart.pdf
ТипДокументы
#1127485
страница12 из 40
1   ...   8   9   10   11   12   13   14   15   ...   40
animals = ['cat', 'dog', 'moose']
>>> for i, animal in enumerate(animals):
... print(i, animal)
0 cat
1 dog
2 moose
Код с enumerate()
получается чуть более чистым, чем код с range(len())
. Если вам нужны только элементы, но не индексы, все равно можно напрямую перебрать список питоническим способом:
>>> # Пример питонического кода
>>> animals = ['cat', 'dog', 'moose']
>>> for animal in animals:
... print(animal)
cat dog moose
Вызов enumerate()
с прямым перебором последовательности предпочтительнее использования устаревшей схемы range(len())
Использование команды with вместо open() и close()
Функция open()
возвращает объект файла, содержащий методы для чтения и запи си в файл. После завершения работы метод close()
объекта файла делает файл до- ступным для чтения и записи со стороны других программ. Эти функции можно использовать по отдельности, но такой подход не соответствует питоническому стилю. Например, введите следующий фрагмент в интерактивной оболочке, чтобы вывести текст «Hello, world!» в файл с именем spam.txt
:

122
Глава 6.Написание питонического кода
>>> # Пример непитонического кода
>>> fileObj = open('spam.txt', 'w')
>>> fileObj.write('Hello, world!')
13
>>> fileObj.close()
Такой код может привести к тому, что файл останется открытым — скажем, если в блоке try возникнет ошибка и вызов close()
будет пропущен. Пример:
>>> # Пример непитонического кода
>>> try:
... fileObj = open('spam.txt', 'w')
... eggs = 42 / 0 # Здесь происходит ошибка деления на нуль.
... fileObj.close() # Эта строка никогда не выполняется.
... except:
... print('Some error occurred.')
Some error occurred.
При возникновении ошибки деления на нуль выполнение передается в блок except
, вызов close()
пропускается, а файл останется открытым. Позднее это может при- вести к повреждению структуры файла, причины которой будет трудно связать с блоком try
Но можно воспользоваться командой with
, чтобы функция close()
автоматически вызывалась при выходе управления из блока with
. Следующий питонический при- мер делает то же самое, что и первый пример в этом разделе:
>>> # Пример питонического кода.
>>> with open('spam.txt', 'w') as fileObj:
... fileObj.write('Hello, world!')
Несмотря на отсутствие явного вызова close()
, команда with обязательно вызовет ее при выходе управления за пределы блока.
Использование is для сравнения с None вместо ==
Оператор
==
сравнивает значения двух объектов, тогда как оператор is сравнивает два объекта на тождественность. Различия между равенством и тождественно- стью объясняются в главе 7. Два объекта могут хранить одинаковые значения, но если это два разных объекта, они не тождественны. Однако при сравнении значения с
None практически всегда следует использовать оператор is вместо оператора
==
В некоторых случаях выражение spam
==
None может дать результат
True
, даже если spam всего лишь содержит
None
. Это может произойти из-за перегрузки оператора
==

Форматирование строк
123
(эта тема более подробно рассматривается в главе 17). Но выражение spam is
None про- верит, является ли значение, хранящееся в переменной spam
, буквальным значением
None
. Так как
None является единственным значением типа данных
NoneType
, в любой программе Python существует только один объект
None
. Если переменной присвоено значение
None
, сравнение is
None всегда дает результат
True
. Особенности перегрузки оператора
==
рассматриваются в главе 17, но ниже приведен пример такого кода:
>>> class SomeClass:
... def __eq__(self, other):
... if other is None:
... return True
>>> spam = SomeClass()
>>> spam == None
True
>>> spam is None
False
Вероятность того, что класс перегружает оператор
==
подобным образом, невелика, но в Python стало идиоматичным всегда использовать is
None вместо
==
None про- сто на всякий случай.
Наконец, оператор is не следует использовать со значениями
True и
False
. Оператор проверки равенства
==
может использоваться для сравнения значения с
True или
False
(например, spam
==
True или spam
==
False
). Еще чаще оператор и логическое значение полностью опускаются, а код записывается в виде if spam:
или if not spam:
вместо if spam
==
True:
или if spam
==
False:
Форматирование строк
Строки встречаются почти во всех компьютерных программах независимо от языка.
Это весьма распространенный тип данных, и не приходится удивляться тому, что существует множество подходов к выполнению строковых операций и формати- рованию строк. В этом разделе я показываю пару приемов.
Использование необработанных строк, если строка содержит
много символов \ (обратный слэш)
Escape-символы позволяют вставлять в строковые литералы текст, который в про- тивном случае было бы невозможно включить
1
. Например, в строку 'Zophie\'s
1
Escape-последовательности, то есть последовательности, которые начинаются с симво- ла «\», за которым следует один или более символов, также называют экранированными последовательностями. — Примеч. ред.

124
Глава 6.Написание питонического кода chair'
необходимо включить символ
\
, чтобы Python интерпретировал вторую кавычку как часть строки, а не как символ, завершающий конец строки. Так как символ
\
является escape-символом, если вы хотите включить литеральный сим- вол
\
в строку, его необходимо ввести в виде
\\
Необработанные (raw) строки представляют собой строковые литералы с префик- сом r
; они не интерпретируют символы
\
как escape-символы. Вместо этого в строку просто включается символ
\
. Например, следующая строка с путем к файлу Windows должна содержать множество экранированных символов
\
, что не соответствует питоническому стилю:
>>> # Пример непитонического кода
>>> print('The file is in C:\\Users\\Al\\Desktop\\Info\\Archive\\Spam')
The file is in C:\Users\Al\Desktop\Info\Archive\Spam
Необработанная строка генерирует то же строковое значение, но читается гораздо лучше:
>>> # Пример питонического кода
>>> print(r'The file is in C:\Users\Al\Desktop\Info\Archive\Spam')
The file is in C:\Users\Al\Desktop\Info\Archive\Spam
Необработанные строки не определяют другой тип строковых данных; это все- го лишь удобный способ записи строковых литералов, содержащих несколько внутренних символов
\
. Необработанные строки часто используются для записи регулярных выражений или путей к файлам Windows с внутренними символами
\
, которые было бы слишком неудобно экранировать по отдельности символами
\\
Форматирование с использованием F-строк
Строковой интерполяцией называется процесс создания строк, включающих другие строки; интерполяция имеет долгую историю в Python. Сначала для конкатенации строк можно было использовать оператор
+
, но это приводило к появлению кода с множеством кавычек и плюсов:
'Hello,
'
+
name
+
'.
Today is
'
+
day
+
'
and it is
'
+
weather
+
'.'
. Спецификатор преобразования
%
несколько упростил этот синтак- сис:
'Hello,
%s.
Today is
%s and it is
%s.'
%
(name,
day,
weather)
. В обоих случаях строки из переменных name,
day и weather вставляются в строковые литералы для генерирования нового строкового значения:
'Hello,
Al.
Today is
Sunday and it is sunny.'
Строковый метод format()
добавляет мини-язык форматных спецификаций
(https://docs.python.org/3/library/string.html#formatspec), в котором пары фигурных скобок
{}
используются способом, напоминающим спецификатор формата
%s
. Тем не менее это довольно запутанный способ, который может привести к созданию нечитаемого кода, поэтому использовать его я не рекомендую.

Поверхностное копирование списков
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(, {'Al': 1})
>>> scores['Zophie'] # Не нужно сначала задавать значение для ключа 'Zophie'.
0
>>> scores['Zophie'] += 40
>>> scores
defaultdict(, {'Al': 1, 'Zophie': 40})

Питонические способы использования словарей
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


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