Почему в Python нет операторов ++ и -?


442

Почему нет там нет ++и --операторы в Python?



потому что там редундент
чмарангу

существует 4 разных оператора ++, которые делают одно и то же. Ох, и там удаление "++" из "C ++" теперь, что похоже на вырождение
cmarangu

Ответы:


443

Это не потому, что это не имеет смысла; имеет смысл определить «x ++» как «x + = 1, оценивая предыдущую привязку x».

Если вы хотите узнать первоначальную причину, вам придется либо просмотреть старые списки рассылки Python, либо спросить кого-то, кто был там (например, Гвидо), но это достаточно легко обосновать после факта:

Простое увеличение и уменьшение не так много, как в других языках. Вы не for(int i = 0; i < 10; ++i)часто пишете такие вещи, как на Python; вместо этого вы делаете вещи, как for i in range(0, 10).

Так как это не нужно почти так же часто, есть гораздо меньше причин для того, чтобы придать ему свой особый синтаксис; когда вам нужно увеличить, +=обычно это нормально.

Это не решение того, имеет ли это смысл, или может ли это быть сделано - оно делает, и может. Вопрос в том, стоит ли добавлять это преимущество в основной синтаксис языка. Помните, что это четыре оператора - postinc, postdec, preinc, precc, и каждый из них должен иметь свои собственные перегрузки класса; все они должны быть указаны и протестированы; это добавило бы к языку операции коды (подразумевающие больший, и, следовательно, более медленный, механизм ВМ); каждый класс, который поддерживает логическое приращение, должен был бы реализовать их (сверху +=и -=).

Это все избыточно +=и -=, так что это станет чистым убытком.


98
Часто полезно использовать что-то вроде array [i ++], что не аккуратно делается с помощью + = / - =.
Тернер Хейс

102
@thayes: Это не распространенный шаблон в Python.
Гленн Мейнард

9
@thayes Так как это будет внутри цикла, вы можете также зацикливаться iнапрямую - если вам это действительно нужно и не можете просто, например, использоватьarray.append()
Tobias Kienzler

31
Я вижу гораздо большую озабоченность в отношении читабельности и предсказуемости. Еще в мои дни C, я видел больше , чем достаточно ошибок , вытекающих из неправильного понимания о различии между i++и ++i...
Charles Duffy

5
В дополнение к оправданию после факта: в проекте, над которым я работаю, я столкнулся (больше, чем кто-либо должен в своей жизни) с достаточным количеством кода на C ++, страдающего от проблем ++и --используемых способами, которые приводят к неопределенным или неопределенным поведение. Они позволяют писать сложный, трудный для правильного анализа код.
Брайан Ванденберг

84

Этот оригинальный ответ, который я написал, является мифом из фольклора компьютерных технологий : Деннис Ритчи назвал его «исторически невозможным», как отмечается в письмах в редакцию журнала ACM, июль 2012 г., doi: 10.1145 / 2209249.2209251


Операторы инкремента / декремента C были изобретены в то время, когда компилятор C не был очень умным, и авторы хотели иметь возможность указать прямое намерение использовать оператор машинного языка, что позволило сэкономить несколько циклов для компилятора, который может сделать

load memory
load 1
add
store memory

вместо

inc memory 

и PDP-11 даже поддерживает команды «автоинкремент» и «отложенный автоинкремент», соответствующие *++pи *p++, соответственно. Смотрите раздел 5.3 руководства, если вам интересно.

Поскольку компиляторы достаточно умны, чтобы справиться с высокоуровневыми приемами оптимизации, встроенными в синтаксис C, они теперь являются лишь синтаксическим удобством.

У Python нет трюков для передачи намерений ассемблеру, потому что он не использует их.


4
Javascript имеет ++. Я не думаю, что это «уловка, чтобы передать намерения ассемблеру». Кроме того, Python имеет байт-код. Поэтому я думаю, что причина в другом.
Натан Дэвис

13
Этот бизнес «предоставления подсказок компилятору» действительно миф. Честно говоря, это глупое дополнение к любому языку, и оно нарушает следующие две заповеди: 1. Вы не кодируете компьютер для чтения, а код другого инженера для чтения. И 2. Вы не пишете код для компетентного инженера, чтобы читать, вы пишете код для компетентного инженера, который читает, когда он истощен в 3 часа ночи и запитан кофеином.

3
@ tgm1024 Если честно, при кодировании полудуплексного телетайпа со скоростью 10–30 символов в секунду вы кодируете его, чтобы его можно было ввести до следующей недели.
Мсв

4
@ tgm1024 Unix и C увидели основную часть своей первоначальной разработки на PDP-11, которые использовали невероятно медленные телетайпы для взаимодействия с пользователем. Хотя вы абсолютно правы в том, что сегодня кодирование для машины в основном глупо, тогда узким местом было именно интерфейс человек / машина. Трудно представить, что работаешь так медленно, если тебе никогда не приходилось.
MSW

65

Я всегда предполагал, что это связано с этой строкой дзен питона:

Должен быть один - и желательно только один - очевидный способ сделать это.

x ++ и x + = 1 делают одно и то же, поэтому нет оснований для того и другого.


10
one--это ноль?
Андре Хольцнер

25
one--один в предложении, но сразу после этого ноль. Таким образом, этот «коан» также намекает на то, что операторы увеличения / уменьшения неочевидны.
Виктор К

14
@EralpB Если вы удалите + =, то вы не сможете делать такие вещи, как x + = 10. + = более общий случай ++
Рори

8
Также: «Явное лучше, чем неявное».
Бер

2
Определенно не то же самое, потому что x + = 1 НЕ является выражением - это утверждение - и оно ни к чему не приводит. Вы не можете делать такие вещи, как: 'row [col ++] = a; строка [col ++] = b '. Не говоря уже о pre-inc и post-inc материалах, которые есть у c ++.
Ури

38

Конечно, мы могли бы сказать: «Гвидо просто так решил», но я думаю, что вопрос действительно в причинах этого решения. Я думаю, что есть несколько причин:

  • Он смешивает выражения и выражения, что не является хорошей практикой. Смотрите http://norvig.com/python-iaq.html
  • Как правило, это побуждает людей писать менее читаемый код
  • Дополнительная сложность в языковой реализации, которая не нужна в Python, как уже упоминалось

12
Рад, что кто-то, наконец, упомянул утверждение против выражения аспекта. В Си присваивание является выражением и, следовательно, оператором ++. В Python присваивание - это оператор, поэтому, если у него есть ++, он, вероятно, тоже должен быть оператором присваивания (и даже менее полезным или необходимым).
Мартино

Согласились - если бы они были заявлениями, то как минимум было бы совершенно бессмысленно говорить о разнице между пост- и предоператорами.
Crowman

17

Потому что в Python целые числа неизменны (int + = фактически возвращает другой объект).

Кроме того, с ++ / - вам нужно беспокоиться о предварительном увеличении / уменьшении, и для его написания требуется всего лишь еще одно нажатие клавиши x+=1. Другими словами, это позволяет избежать потенциальной путаницы за счет очень небольшого выигрыша.


Интты также неизменны в Си. Если вы не думаете так, попытайтесь получить C компилятор для генерации коды для 42++... Что - то вроде этого (модифицирующую буквальная константы) был на самом деле возможно в некоторых старых Фортране (или так я прочитал): Все виды использования в будущем из этого литерала в этом прогоне программы будет действительно иметь другое значение. Удачной отладки!
Лутц Пречелт

10
Правильно. 42 буквальная константа . Константы являются (или, по крайней мере, должны быть) неизменяемыми. Это не значит, что C intвообще неизменны. Буква intC просто обозначает место в памяти. И биты в этом месте очень изменчивы. Вы можете, например, создать ссылку intи изменить референт этой ссылки. Это изменение видно во всех ссылках (включая исходную intпеременную) на это место. То же самое не относится к целочисленному объекту Python.
Натан Дэвис,

x + = 1 не в конечном итоге становится inc mem. Python выполняет вызов функции, чтобы добавить 1 к переменной; это жалко
PalaDolphin

12

Ясность!

Python много говорит о ясности, и ни один программист, вероятно, не сможет правильно угадать значение, --aесли он не изучил язык с такой конструкцией.

В Python также много избегают конструкций, которые допускают ошибки, и ++операторы, как известно, являются богатыми источниками дефектов. Этих двух причин недостаточно для того, чтобы в Python не было этих операторов.

Решение о том, что Python использует отступы для маркировки блоков, а не синтаксические средства, такие как некоторая форма начальных / конечных скобок или обязательная конечная маркировка, основано в основном на тех же соображениях.

Для иллюстрации взгляните на обсуждение вопроса о введении условного оператора (в C cond ? resultif : resultelse:) в Python в 2005 году. Прочитайте, по крайней мере, первое сообщение и сообщение о решении этого обсуждения (в котором ранее было несколько предшественников по той же теме).

Общая информация: PEP, часто упоминаемый в этом документе, представляет собой «Предложение по расширению Python» PEP 308 . LC означает понимание списка , GE означает выражение генератора (и не волнуйтесь, если они вас смущают, это не одно из немногих сложных мест Python).


10

Мое понимание того, почему в python нет ++оператора, заключается в следующем: когда вы пишете это в python, a=b=c=1вы получите три переменные (метки), указывающие на один и тот же объект (значение которого равно 1). Вы можете проверить это с помощью функции id, которая будет возвращать адрес памяти объекта:

In [19]: id(a)
Out[19]: 34019256

In [20]: id(b)
Out[20]: 34019256

In [21]: id(c)
Out[21]: 34019256

Все три переменные (метки) указывают на один и тот же объект. Теперь увеличьте одну переменную и посмотрите, как она влияет на адреса памяти:

In [22] a = a + 1

In [23]: id(a)
Out[23]: 34019232

In [24]: id(b)
Out[24]: 34019256

In [25]: id(c)
Out[25]: 34019256

Вы можете видеть, что переменная aтеперь указывает на другой объект как переменные bи c. Потому что вы использовали a = a + 1это явно ясно. Другими словами, вы назначаете совершенно другой объект для метки a. Представьте, что вы можете написать, a++это будет означать, что вы не присваиваете переменнуюa новый объект, но, скорее всего, увеличиваете старый. Все это мелочи ИМХО для минимизации путаницы. Для лучшего понимания посмотрите, как работают переменные Python:

В Python, почему функция может изменять некоторые аргументы так, как они воспринимаются вызывающей стороной, но не другие?

Является ли Python вызовом по значению или вызовом по ссылке? Ни.

Python передает по значению или по ссылке?

Является ли Python передачей по ссылке или передачей по значению?

Python: как передать переменную по ссылке?

Понимание переменных Python и управления памятью

Эмуляция поведения передачи по значению в Python

Вызов функций Python по ссылке

Код как Pythonista: идиоматический Python


9

Это было разработано именно так. Операторы увеличения и уменьшения - просто ярлыки для x = x + 1. Python обычно принимает стратегию проектирования, которая уменьшает количество альтернативных способов выполнения операции. Дополненное присваивание - самая близкая вещь к операторам увеличения / уменьшения в Python, и они даже не были добавлены до Python 2.0.


3
Да, приятель, ты мог бы заменить return a[i++]на return a[i=i+1].
Луис де Соуза

7

Я очень плохо знаком с Python, но подозреваю, что причина в том, что в языке делается акцент на изменяемые и неизменяемые объекты. Теперь, я знаю , что х ++ легко можно интерпретировать как х = х + 1, но это выглядит , как вы приращение на месте объекта , который может быть неизменен.

Просто мое предположение / чувство / догадка.


В этом аспекте, x++ближе к x += 1чем x = x + 1, эти два также имеют значение для изменяемых объектов.
glglgl

4

Во-первых, Python только косвенно зависит от C; он сильно подвержен влиянию ABC , у которого, по- видимому, нет этих операторов , поэтому неудивительно, что они не найдены в Python.

Во-вторых, как уже говорили другие, увеличение и уменьшение поддерживаются +=и -=уже.

В-третьих, полная поддержка набора операторов ++и --операторов обычно включает в себя поддержку их префиксной и постфиксной версий. В C и C ++ это может привести ко всем видам «прекрасных» конструкций, которые, как мне кажется, противоречат духу простоты и прямолинейности, который охватывает Python.

Например, хотя оператор C while(*t++ = *s++);может показаться простым и элегантным опытному программисту, для кого-то, кто его изучает, это совсем не просто. Добавьте смесь префиксов и постфиксных приращений и уменьшений, и даже многим профессионалам придется остановиться и задуматься.


3

Я считаю, что это вытекает из убеждения Python, что «явное лучше, чем неявное».


Ну, вы явно не пишете операторы "begin" и "end" в Python, верно? Хотя я согласен с этим утверждением, я думаю, что у этого есть границы. Хотя мы можем спорить об этих границах, я думаю, что мы все можем согласиться с тем, что существует линия, которую нецелесообразно пересекать. И так как есть много мнений и обоснований по поводу этого решения, я не думаю, что это был четкий выбор. По крайней мере, я не могу найти источник, где это явно указано
Хасан Аммори

3

Это может быть из-за того, что @GlennMaynard смотрит на этот вопрос по сравнению с другими языками, но в Python вы делаете все по-Питонски. Это не вопрос «почему». Это там, и вы можете делать вещи с тем же эффектом x+=. В дзен Python сказано: «должен быть только один способ решить проблему». Многочисленные варианты хороши в искусстве (свобода выражения мнений), но отвратительны в технике.


2

++Класс операторов выражение с побочными эффектами. Это то, чего обычно нет в Python.

По той же причине присваивание не является выражением в Python, что препятствует распространению if (a = f(...)) { /* using a here */ }идиомы.

Наконец, я подозреваю, что там операторы не очень согласуются с семантикой ссылок Pythons. Помните, что в Python нет переменных (или указателей) с семантикой, известной из C / C ++.


1
ничто не мешает вызвать функцию с побочным эффектом в понимании test / expression / list: f(a)где aсписок, какой-то неизменный объект.
Жан-Франсуа Фабр

1

Возможно, лучше задать вопрос: почему эти операторы существуют в C. Операции приращения и убывания вызовов K & R «необычны» (Раздел 2.8, стр. 46). Введение называет их «более краткими и зачастую более эффективными». Я подозреваю, что тот факт, что эти операции всегда возникают при манипуляции с указателями, также сыграл свою роль в их введении. В Python, вероятно, было решено, что нет смысла пытаться оптимизировать приращения (на самом деле я только что провел тест на C, и кажется, что сгенерированная gcc сборка использует addl вместо incl в обоих случаях) и нет арифметика указателей; так что это был бы просто еще один способ сделать это, и мы знаем, что Python ненавидит это.


1

как я понял, вы не будете думать, что значение в памяти изменилось. в c, когда вы делаете x ++, значение x в памяти изменяется. но в python все числа неизменны, поэтому адрес, на который указывает x, все еще имеет x, а не x + 1. когда вы пишете x ++, вы можете подумать, что x изменит то, что действительно происходит, так это то, что x refrence заменяется местом в памяти, где хранится x + 1, или воссоздаете это место, если doe не существует.


1
Так чем же это ++отличается += 1?
Бер

1

Чтобы завершить уже хорошие ответы на этой странице:

Давайте предположим, что мы решили сделать это, префикс (++i ), который сломал бы унарные операторы + и -.

Сегодня, префикс ++или --ничего не делает, потому что он включает унарный оператор плюс дважды (ничего не делает) или унарный минус дважды (дважды: отменяет себя)

>>> i=12
>>> ++i
12
>>> --i
12

Так что это может нарушить эту логику.


1

В других ответах описано, почему это не нужно для итераторов, но иногда это полезно при назначении для увеличения переменной в строке, вы можете добиться того же эффекта, используя кортежи и множественное присваивание:

b = ++a будет выглядеть так:

a,b = (a+1,)*2

и b = a++становится:

a,b = a+1, a

Python 3.8 вводит задание :=оператора, что позволяет нам достичь foo(++a)с

foo(a:=a+1)

foo(a++) все еще неуловимо все же.


2
: = назначение - позор
неем

0

Я думаю, что это относится к понятиям изменчивости и неизменности объектов. 2,3,4,5 неизменны в питоне. Обратитесь к изображению ниже. 2 имеет фиксированный идентификатор до этого процесса Python.

ID констант и переменных

x ++ по существу означал бы приращение на месте, подобное C. В C x ++ выполняет приращения на месте. Таким образом, x = 3, а x ++ увеличивает значение 3 в памяти до 4, в отличие от python, где 3 все еще существует в памяти.

Таким образом, в python вам не нужно воссоздавать значение в памяти. Это может привести к оптимизации производительности.

Это ответ на догадку.


0

Я знаю, что это старый поток, но наиболее распространенный вариант использования для ++ i не рассматривается, это ручная индексация наборов, когда нет предоставленных индексов. Именно поэтому Python предоставляет enumerate ()

Пример: в любом конкретном языке, когда вы используете конструкцию наподобие foreach для итерации по набору - в качестве примера мы даже скажем, что это неупорядоченный набор, и вам нужен уникальный индекс для всего, чтобы отличить их, скажем,

i = 0
stuff = {'a': 'b', 'c': 'd', 'e': 'f'}
uniquestuff = {}
for key, val in stuff.items() :
  uniquestuff[key] = '{0}{1}'.format(val, i)
  i += 1

В подобных случаях python предоставляет метод перечисления, например

for i, (key, val) in enumerate(stuff.items()) :
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.