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

  • Почему 256 — это 256, а 257 — не 257

  • Фиктивные операторы инкремента и декремента в языке Python

  • Логические значения как целые числа

  • True + False + True + True

  • True is False False>>> True = False >>> True is False

  • Сцепление разных видов операторов

  • False == False in [False]

  • Антигравитация в Python Введите следующую команду в интерактивной оболочке:>>> import antigravity

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


    Скачать 7.85 Mb.
    НазваниеЧистыйкод дляпродолжающи х
    Дата13.05.2023
    Размер7.85 Mb.
    Формат файлаpdf
    Имя файлаPython_Chisty_kod_dlya_prodolzhayuschikh_2022_El_Sveygart.pdf
    ТипДокументы
    #1127485
    страница19 из 40
    1   ...   15   16   17   18   19   20   21   22   ...   40
    Итоги
    Несогласованность встречается в любом языке, даже в языках программирова- ния. Python содержит несколько потенциальных ловушек, которые подстерегают невнимательных. И хотя они встречаются достаточно редко, лучше знать о них заранее. Это позволит вам быстро опознать и устранить проблемы, которые могут из-за них возникнуть.
    Хотя теоретически элементы можно добавлять и удалять из списка в процессе пере- бора этого списка, это потенциальный источник ошибок. Намного безопаснее пере- брать копию списка, а затем внести изменения в оригинал. Создавая копии списка
    (или любого другого изменяемого объекта), помните, что команды присваивания копируют только ссылку на объект, а не сам объект. Для создания копий объекта
    (и копий всех объектов, ссылки на которые в нем хранятся) можно воспользоваться функцией copy.deepcopy()

    184
    Глава 8.Часто встречающиеся ловушки Python
    Изменяемые объекты не должны использоваться в командах def для аргументов по умолчанию, потому что они создаются однократно при выполнении команды def
    , а не при каждом вызове функции. Лучше использовать в качестве аргумента по умолчанию
    None и добавить код, который проверяет
    None и создает изменяемый объект при вызове функции.
    Еще одна коварная ловушка возникает при конкатенации нескольких строк опе- ратором
    +
    в цикле. При небольшом количестве итераций этот синтаксис работает нормально. Но «под капотом» Python постоянно создает и уничтожает объекты строк при каждой итерации. Более эффективное решение — присоединять меньшие строки к списку, а затем вызвать оператор join()
    для создания итоговой строки.
    Метод sort()
    сортирует элементы по числовым кодовым пунктам, и этот порядок не совпадает с алфавитным: буква Z в верхнем регистре предшествует букве a в нижнем регистре. Проблему можно решить вызовом sort(key=str.lower)
    У чисел с плавающей точкой возникают небольшие погрешности — это побочный эффект способа представления чисел в памяти компьютера. Для большинства программ эти ошибки не важны. Но если для ваших задач это важно, вы можете воспользоваться модулем Python decimal
    Никогда не объединяйте операторы
    !=
    в цепочки, потому что, как ни странно, вы- ражения вида 'cat'
    !=
    'dog'
    !=
    'cat'
    дают результат
    True
    Хотя в этой главе описаны ловушки Python, которые вы наверняка встретите в ра- боте, они не попадаются ежедневно в реальном коде. Python всеми силами старается свести к минимуму неприятные неожиданности в ваших программах. В следующей главе я расскажу о ловушках еще более редких и экзотических. Вероятность того, что они когда-нибудь попадутся на вашем пути, близка к нулю, и все же вам на- верняка будет любопытно исследовать причины их существования.

    9
    Экзотические странности Python
    Системы правил, определяющие язык программирования, достаточно сложны. Порой они приводят к появлению кода хотя и не ошибочного, но достаточно странного и неожидан- ного. В этой главе мы займемся некоторыми малоизвестными странностями языка Python. Вряд ли вы когда-нибудь столк- нетесь с ними в повседневном программировании, но их можно рассматривать как неочевидное применение синтаксиса Python (или злоупотребление им — зависит от точки зрения).
    Изучая примеры этой главы, вы начнете лучше представлять, как работают внутрен- ние механизмы Python. Давайте исследуем некоторые экзотические случаи — из интереса и для расширения кругозора.
    Почему 256 — это 256, а 257 — не 257
    Оператор
    ==
    проверяет, равны ли два объекта, а оператор is проверяет их на тож- дественность, то есть сравнивает их идентичности. И хотя целое число
    42
    и число с плавающей точкой
    42.0
    имеют одинаковое значение, это два разных объекта, хранящихся в разных местах компьютерной памяти. Чтобы убедиться в этом, про- верьте их идентичности при помощи функции id()
    :
    >>> a = 42
    >>> b = 42.0
    >>> a == b
    True
    >>> a is b
    False
    >>> id(a), id(b)
    (140718571382896, 2526629638888)

    186
    Глава 9.Экзотические странности Python
    Когда Python создает новый объект, представляющий целое число, и сохра- няет его в памяти, создание объекта занимает очень мало времени. CPython
    (интерпретатор Python, доступный для загрузки по адресу https://python.org) применяет крошечную оптимизацию, создавая объекты целых чисел от –5 до
    256 при запуске каждой программы. Эти числа называются предварительно
    определенными (preallocated), и CPython автоматически создает для них объекты, потому что они довольно часто применяются: число 0 или 2 будет использовано в программе с большей вероятностью, чем, скажем, 1729. При создании объекта нового целого числа в памяти CPython сначала проверяет, принадлежит ли оно диапазону от –5 до 256. В таком случае CPython экономит время, просто воз- вращая существующий объект целого числа, вместо того чтобы создавать его заново. Такое поведение также экономит память, так как она не расходуется на хранение малых целых чисел (рис. 9.1).
    Рис. 9.1. Для экономии памяти Python использует множественные ссылки на один объект целого числа (слева) вместо дублирующихся объектов целых чисел для каждой ссылки (справа)
    Из-за этой оптимизации некоторые нетипичные ситуации могут приводить к стран- ным результатам. Чтобы увидеть пример такого рода, введите следующий фрагмент в интерактивной оболочке:
    >>> a = 256
    >>> b = 256
    >>> a is b

    True
    >>> c = 257
    >>> d = 257
    >>> c is d

    False
    Все объекты, представляющие целое число
    256
    , в действительности являются од- ним объектом, поэтому для a
    и b
    оператор возвращает
    True

    . Но для c
    и d
    Python создает разные объекты
    257
    , поэтому оператор is возвращает
    False

    Выражение
    257
    is
    257
    дает результат
    True
    , потому что CPython повторно исполь- зует объект целого числа, созданного для идентичного литерала в той же команде:

    Интернирование строк
    187
    >>> 257 is 257
    True
    Конечно, в реальных программах обычно используется только значение целого числа, а не его идентичность. Программы никогда не используют оператор is для сравнения целых чисел, чисел с плавающей точкой, строк, логических значений или значений других простых типов данных. Одно из исключений такого рода встречается при использовании is
    None вместо
    ==
    None
    , и я объясняю это в под- разделе «Использование is для сравнения с None вместо ==», с. 122. В остальных ситуациях это исключение почти не встречается.
    Интернирование строк
    Аналогичным образом Python повторно использует строки для представления идентичных строковых литералов в вашем коде вместо создания нескольких копий одной строки. Чтобы убедиться в этом, введите следующий фрагмент в интерак- тивной оболочке:
    >>> spam = 'cat'
    >>> eggs = 'cat'
    >>> spam is eggs
    True
    >>> id(spam), id(eggs)
    (1285806577904, 1285806577904)
    Python замечает, что строковый литерал 'cat'
    , присвоенный eggs
    , совпадает со строковым литералом 'cat'
    , присвоенным spam
    ; таким образом, вместо второго, избыточного объекта string переменной eggs просто присваивается ссылка на тот же объект строки, который используется spam
    . Этим объясняется совпадение идентичностей строк.
    Такая оптимизация называется интернированием (string interning) строк. Как и предварительное определение целых чисел, это всего лишь подробность реа- лизации CPython. Никогда не пишите код, который зависит от нее. Кроме того, эта оптимизация не обнаруживает все возможные идентичные строки. Попытки идентифицировать все экземпляры, к которым можно применить оптимизацию, часто занимают больше времени, чем сэкономит оптимизация. Например, попро- буйте создать строку 'cat'
    из 'c'
    и 'at'
    в интерактивной оболочке; вы заметите, что CPython создает итоговую строку 'cat'
    как новый объект строки, вместо того чтобы повторно использовать объект строки, созданный для spam
    :
    >>> bacon = 'c'
    >>> bacon += 'at'
    >>> spam is bacon
    False

    188
    Глава 9.Экзотические странности Python
    >>> id(spam), id(bacon)
    (1285806577904, 1285808207384)
    Интернирование строк — метод оптимизации, применяемый интерпретаторами и компиляторами для многих языков программирования. За дополнительной ин- формацией обращайтесь на https://en.wikipedia.org/wiki/String_interning.
    Фиктивные операторы инкремента и декремента
    в языке Python
    В Python можно увеличить значение переменной на 1 или уменьшить его на 1 при помощи расширенных операторов присваивания. Выражения spam
    +=
    1
    и spam
    -=
    1
    увеличивают и уменьшают числовые значения в spam на 1 соответственно.
    В других языках, таких как C++ и JavaScript, существуют операторы
    ++
    и
    -- для выполнения инкремента и декремента. (Этот факт отражен в самом названии языка C++; это иронический намек на то, что язык является улучшенной формой языка C.) В коде на языках C++ и JavaScript могут встречаться такие операции, как
    ++spam или spam++
    . Создатели Python благоразумно не стали включать в язык эти операторы, получившие печальную известность из-за коварных ошибок (см. https://
    softwareengineering.stackexchange.com/q/59880).
    Тем не менее следующий код Python вполне допустим:
    >>> spam = --spam
    >>> spam
    42
    Первое, на что следует обратить внимание: операторы
    ++
    и
    -- в Python не уве- личивают и не уменьшают значение, хранимое в переменной spam
    . Начальный знак
    - является унарным оператором отрицания Python. Он позволяет писать код следующего вида:
    >>> spam = 42
    >>> -spam
    -42
    Ничто не запрещает поставить перед значением несколько унарных операторов отрицания. С двумя операторами выполняется отрицание отрицания значения, что для целых чисел просто дает исходное значение:
    >>> spam = 42
    >>> -(-spam)
    42

    Все или ничего
    189
    Это очень глупая операция, и, скорее всего, вы никогда не увидите двукратное ис- пользование унарного оператора отрицания в реальном коде. (А если увидите, то, наверное, программист учился программировать на другом языке и просто написал ошибочный код на Python!)
    Также существует унарный оператор
    +
    . Он создает целое значение с таким же зна- ком, как у исходного значения, то есть по сути не делает ничего:
    >>> spam = 42
    >>> +spam
    42
    >>> spam = -42
    >>> +spam
    -42
    Запись
    +42
    (или
    ++42
    ) выглядит так же глупо, как и
    --42
    , так почему же в Python был включен этот унарный оператор? Он существует только как парный по от- ношению к оператору
    -
    , если вам потребуется перегрузить эти операторы в ваших классах. (Здесь много терминов, которые могут быть вам незнакомы. Перегрузка операторов более подробно рассматривается в главе 17.)
    Унарные операторы
    +
    и
    - могут располагаться только перед значением Python, но не после него. И хотя выражения spam++
    и spam-- могут быть действительным кодом в C++ или JavaScript, в Python они вызывают синтаксические ошибки:
    >>> spam++
    File "", line 1
    spam++
    ^
    SyntaxError: invalid syntax
    В Python нет операторов инкремента и декремента. Эта странность языкового синтаксиса только создает иллюзию того, что они существуют.
    Все или ничего
    Встроенная функция all()
    получает значение-последовательность (например, список) и возвращает
    True
    , если все значения в этой последовательности являются истинными. Если хотя бы одно или несколько значений являются ложными, воз- вращается
    False
    . Вызов функции all([False,
    True,
    True])
    можно рассматривать как эквивалент выражения
    False and
    True and
    True
    Функция all()
    может использоваться в сочетании со списковыми включениями, для того чтобы сначала создать список логических значений на основании другого

    190
    Глава 9.Экзотические странности Python списка, а затем вычислить их сводное значение. Например, введите следующий фрагмент в интерактивной оболочке:
    >>> spam = [67, 39, 20, 55, 13, 45, 44]
    >>> [i > 42 for i in spam]
    [True, False, False, True, False, True, True]
    >>> all([i > 42 for i in spam])
    False
    >>> eggs = [43, 44, 45, 46]
    >>> all([i > 42 for i in eggs])
    True
    Функция all()
    возвращает
    True
    , если все числа в spam или eggs больше 42.
    Но если передать all()
    пустую последовательность, функция всегда возвращает
    True
    . Введите следующий фрагмент в интерактивной оболочке:
    >>> all([])
    True
    Правильнее считать, что all([])
    проверяет утверждение «ни один из элементов списка не является ложным» (вместо «все элементы списка являются истинными»).
    В противном случае вы можете получить неожиданные результаты. Например, введите следующий фрагмент в интерактивной оболочке:
    >>> spam = []
    >>> all([i > 42 for i in spam])
    True
    >>> all([i < 42 for i in spam])
    True
    >>> all([i == 42 for i in spam])
    True
    Пример вроде бы показывает, что все значения в spam
    (пустой список!) не только больше 42, но они одновременно меньше 42 и равны 42! Это кажется невозможным с точки зрения логики. Но помните, что каждое из трех списковых включений при вычислении дает пустой список. В нем нет ни одного ложного элемента, а все функции all()
    возвращают
    True
    Логические значения как целые числа
    Подобно тому как Python считает, что значение с плавающей точкой
    42.0
    равно целому значению
    42
    , логические значения
    True и
    False считаются эквивалентными
    1
    и
    0
    соответственно. В Python тип данных bool является субклассом типа данных int
    . (Классы и субклассы рассматриваются в главе 16.) Вы можете воспользоваться int()
    для преобразования логических значений в целые числа:

    Логические значения как целые числа
    191
    >>> int(False)
    0
    >>> int(True)
    1
    >>> True == 1
    True
    >>> False == 0
    True
    Вы также можете использовать isinstance()
    , чтобы подтвердить, что логическое значение относится к типу integer
    :
    >>> isinstance(True, bool)
    True
    >>> isinstance(True, int)
    True
    Значение
    True относится к типу данных bool
    . Но так как bool является субклассом int
    ,
    True также является частным случаем int
    . Это означает, что
    True и
    False мо- гут использоваться практически в любом месте, где допустимо применение целых чисел. Это порождает странный код:
    >>> True + False + True + True # То же, что 1 + 0 + 1 + 1 3
    >>> -True # То же, что -1.
    -1
    >>> 42 * True # То же, что математическое умножение 42 * 1.
    42
    >>> 'hello' * False # То же, что репликация строк 'hello' * 0.
    ' '
    >>> 'hello'[False] # То же, что 'hello'[0]
    'h'
    >>> 'hello'[True] # То же, что 'hello'[1]
    'e'
    >>> 'hello'[-True] # То же, что 'hello'[-1]
    'o'
    Конечно, из того, что значения bool могут использоваться как числа, не следует, что это стоит делать. Все приведенные примеры очень плохо читаются и никогда не должны использоваться в реальном коде. Изначально в Python не было типа данных bool
    . Логический тип появился только в Python 2.3, когда тип bool был определен как субкласс int для упрощения реализации. С историей типа дан- ных bool можно ознакомиться в PEP 285 на https://www.python.org/dev/peps/
    pep-0285/.
    Кстати говоря,
    True и
    False стали ключевыми словами только в Python 3. А следо- вательно, в Python 2
    True и
    False можно было использовать как имена переменных, что приводит к появлению парадоксального кода следующего вида:

    192
    Глава 9.Экзотические странности Python
    Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:25:58) [MSC v.1500 64 bit
    (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> True is False
    False
    >>> True = False
    >>> True is False
    True
    К счастью, подобный запутанный код невозможен в Python 3, и при попытке ис- пользовать ключевые слова
    True и
    False в качестве имен переменных происходит синтаксическая ошибка.
    Сцепление разных видов операторов
    Цепочки разных операторов в одном выражении могут привести к неожиданным ошибкам. Например, следующий (откровенно говоря, нереалистичный) пример использует операторы
    ==
    и in в одном выражении:
    >>> False == False in [False]
    True
    Результат
    True выглядит удивительно, потому что можно ожидать, что выражение будет вычисляться по одной из следующих схем.
    (False
    ==
    False)
    in
    [False]
    , дает результат
    False
    False
    ==
    (False in
    [False])
    , также дает результат
    False
    Но выражение
    False
    ==
    False in
    [False]
    не эквивалентно ни одному из этих вы- ражений. Вместо этого оно эквивалентно
    (False
    ==
    False)
    and
    (False in
    [False])
    , подобно тому как
    42
    <
    spam
    <
    99
    эквивалентно
    (42
    <
    spam)
    and
    (spam
    <
    99)
    . Это вы- ражение вычисляется по следующей диаграмме:
    (False == False) and (False in [False])

    (True) and (False in [False])

    (True) and (True)

    True
    Выражение
    False
    ==
    False in
    [False]
    скорее является любопытной головоломкой на языке Python, но вряд ли вы когда-нибудь встретите его в реальном коде.

    Итоги
    193
    Антигравитация в Python
    Введите следующую команду в интерактивной оболочке:
    >>> import antigravity
    Эта строка — забавная пасхалка, которая открывает в браузере классический комикс
    XKCD о Python (https://xkcd.com/353/). Вас может удивить, что Python открывает ваш браузер, но это встроенная возможность, предоставляемая модулем webbrowser
    Модуль Python webbrowser содержит функцию open()
    , которая находит браузер по умолчанию вашей операционной системы и открывает его окно с заданным URL- адресом. Введите следующую команду в интерактивной оболочке:
    >>>
    1   ...   15   16   17   18   19   20   21   22   ...   40


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