Оператор «is» Python ведет себя неожиданно с целыми числами?
blockquote>Вкратце - позвольте мне подчеркнуть: Не используйте
is
для сравнения целых чисел.Это не поведение, о котором вы должны ожидать.
Вместо этого используйте
==
и!=
для сравнения для равенства и неравенства соответственно. Например:>>> a = 1000 >>> a == 1000 # Test integers like this, True >>> a != 5000 # or this! True >>> a is 1000 # Don't do this! - Don't use `is` to test integers!! False
Объяснение
Чтобы это знать, вам нужно знать следующее.
Во-первых, что делает
is
? Это оператор сравнения. Из документации :Операторы
blockquote>is
иis not
проверяют идентификатор объекта:x is y
истинно тогда и только тогда, когда x и y являются одинаковыми объект.x is not y
дает обратное значение истины.И поэтому следующие эквиваленты.
>>> a is b >>> id(a) == id(b)
blockquote>
id
Вернуть «идентификатор» объекта. Это целое число (или длинное целое число), которое гарантировано будет уникальным и постоянным для этого объекта в течение его жизни. Два объекта с неперекрывающимся временем жизни могут иметь одинаковое значениеid()
.Обратите внимание, что тот факт, что идентификатор объекта в CPython (эталонная реализация Python) - это местоположение в память - это деталь реализации. Другие реализации Python (например, Jython или IronPython) могут легко иметь другую реализацию для
id
.Итак, каков прецедент для
is
? PEP8 описывает :. Сравнение с синглонами типа
None
всегда должно выполняться с помощьюis
илиis not
, никогда не выполняемых операторов равенства.Вопрос
Вы задаете и задаете следующий вопрос (с кодом):
Почему в Python происходит непредвиденное поведение?
blockquote>>>> a = 256 >>> b = 256 >>> a is b True # This is an expected result
Ожидаемый результат not . Почему это ожидалось? Это означает, что целые числа, оцененные в
256
, на которые ссылаются какa
, так иb
, являются одним и тем же экземпляром целого числа. Целые числа неизменны в Python, поэтому они не могут измениться. Это не должно влиять на какой-либо код. Этого нельзя ожидать. Это всего лишь деталь реализации.Но, возможно, мы должны быть рады, что каждый отдельный экземпляр в памяти не будет каждый раз, когда мы укажем, что значение равно 256.
blockquote>>>> a = 257 >>> b = 257 >>> a is b False # What happened here? Why is this False?
Looks например, теперь у нас есть два отдельных экземпляра целых чисел со значением
257
в памяти. Поскольку целые числа неизменны, это отнимает память. Будем надеяться, что мы не будем тратить много денег. Наверное, нет. Но это поведение не гарантируется.blockquote>>>> 257 is 257 True # Yet the literal numbers compare properly
Ну, похоже, что ваша конкретная реализация Python пытается быть умной и не создавать избыточно ценные целые числа в памяти, если только она не имеет к. Вы, кажется, указываете, что используете референтную реализацию Python, которая является CPython. Хорошо для CPython.
Возможно, было бы лучше, если бы CPython мог сделать это глобально, если бы он мог сделать это дешево (так как это будет стоить в поиске), возможно, другая реализация.
Но что касается влияния на код, вам все равно, является ли целое число конкретным экземпляром целого числа. Вы должны знать, что такое значение этого экземпляра, и вы использовали бы для этого обычные операторы сравнения, т. Е.
==
.Что
is
делает
is
проверяет, чтоid
двух объектов одинаковы. В CPythonid
- это место в памяти, но это может быть какое-то другое уникально идентифицирующее число в другой реализации. Чтобы переформулировать это с помощью кода:>>> a is b
совпадает с
>>> id(a) == id(b)
Почему мы хотели бы использовать
is
тогда?Это может быть очень быстрой проверкой, чтобы сказать, проверяя, являются ли две очень длинные строки равными по стоимости. Но поскольку это относится к уникальности объекта, мы, таким образом, имеем ограниченные прецеденты. Фактически, мы в основном хотим использовать его для проверки на
None
, который является одноэлементным (единственный экземпляр, существующий в одном месте в памяти). Мы могли бы создать другие синглтоны, если есть потенциал для их объединения, что мы можем проверить с помощьюis
, но они относительно редки. Вот пример (будет работать в Python 2 и 3), напримерSENTINEL_SINGLETON = object() # this will only be created one time. def foo(keyword_argument=None): if keyword_argument is None: print('no argument given to foo') bar() bar(keyword_argument) bar('baz') def bar(keyword_argument=SENTINEL_SINGLETON): # SENTINEL_SINGLETON tells us if we were not passed anything # as None is a legitimate potential argument we could get. if keyword_argument is SENTINEL_SINGLETON: print('no argument given to bar') else: print('argument to bar: {0}'.format(keyword_argument)) foo()
Что печатает:
no argument given to foo no argument given to bar argument to bar: None argument to bar: baz
Итак, мы видим, что с
is
и дозорным, мы могут различать, когдаbar
вызывается без аргументов и когда он вызывается с помощьюNone
. Это первичные варианты использования дляis
- do not использовать его для проверки равенства целых чисел, строк, кортежей или других подобных вещей.
Используют Функцию Совместного доступа к файлам Системы управления исходным кодом
(Редактирование: повторно названный это для предотвращения беспорядка. , Конечно , все должны использование Управление исходным кодом! :-))
Использование Проект
Я думаю, что никакие специальные решения не требуются. В нашем проекте (несколько приложений, которые совместно используют большие площади кода) мы используем следующий подход:
В нашем случае, мы не могли управлять списком импортированных файлов. Однако мы могли управлять списком импортированных пакетов в разделенных сборках. Меньшие пакеты означают лучшую гранулярность. Если кто-то добавляет зависимость к единице, расположенной в папке, которая не доступна в пути поиска, и пакет, содержащий эту единицу, не находится в списке использования, разделенная сборка является отказавшей. Так, явное действие (изменяющий сценарий MSBuild, который генерирует файлы CFG для разделенной сборки) требуется, чтобы добавлять зависимость.
P.S. Мы используем пакеты для не управления зависимостями, но из-за проблем версий Windows non-NT, запускающих крупные приложения. Так, управление зависимостью является побочным эффектом. Разделенные сборки рассматривают как "выпуск" и монолит - как конфигурация "отладки". Приложения монолита используются только для кодирования и отладки. Разработчики работают с приложениями монолита и представляют свои собственные изменения в конфигурациях проекта как присоединение информации об отладке VCL, включая и выключая ошибки проверки принадлежности к диапазону, оптимизация и т.д. Однако после того, как соглашаются на SVN, CC пытается сделать разделенную сборку. Это игнорирует файлы CFG из репозитория и воссоздает их использующий специальную задачу проекта MSBuild. Таким образом, мы можем быть уверены, что никакие проблемы с зависимостями не были представлены (и выполните другие проверки также).
Насколько нам не нужны монолит и разделенные сборки одновременно, у нас есть только единственный проект на приложение. Если Вы хотите создать обе версии в сценарии MSBuild, Вы могли бы просто добавить еще одну цель, воссоздать CFG еще раз и указать еще один выходной каталог Единицы. Естественно, если бы обе версии требуются для разработчиков, было бы более удобно иметь больше проектов.
Я не уверен, понял ли я вопрос правильно. Так или иначе, при создании пакета приложений (несколько проектов, но партия общего кода), мы создаем структуру папок как это:
\Main
\Project1
\Project2
...
\CommonUnits
Мы добавляем общие единицы к соответствующим проектам (независимо, это не находится в той же папке как файл проекта). Далее, иногда легче использовать условное выражение уровня проекта, определяет (Проект | Опции | Каталоги/Условные выражения) для небольших различий в коде. Например, Project1 будет иметь что-то как "APP_PROJECT1" определенным, и можно затем использовать $IFDEF в общих единицах для написания определенного кода.
, Что важно: в этом случае лучше иметь один репозиторий управления исходным кодом для целого комплекта (корень является \Main, конечно).
Копия на компиляции
Copy-Compile-Delete