파이썬에서 증가 및 감소 연산자의 동작


797

사전 증분 / 감소 연산자를 변수 (예 :)에 적용 할 수 있습니다 ++count. 컴파일되지만 실제로 변수의 값을 변경하지는 않습니다!

파이썬에서 사전 증가 / 감소 연산자 (++ /-)의 동작은 무엇입니까?

왜 파이썬이 C / C ++에서 볼 수있는 이러한 연산자의 동작에서 벗어 납니까?


19
파이썬은 C 또는 C ++이 아닙니다. 언어를 결정하기 위해 다양한 디자인 결정이 내려졌습니다. 특히 파이썬은 의도적으로 임의의 표현에 사용될 수있는 할당 연산자를 정의하지 않습니다. 오히려 할당 문과 확장 된 할당 문이 있습니다. 아래 참조를 참조하십시오.
Ned Deily

8
당신은 파이썬이 가지고 무엇을 생각하게 ++하고 --연산자를?
u0b34a0f6ae

29
Kaizer : C / C ++에서 ++ count를 쓰고 파이썬으로 컴파일합니다. 그래서 언어에는 연산자가 있다고 생각했습니다.
Ashwin Nanjappa

3
@Fox 당신은 증거가 아닌 수준의 계획과 조직을 가정하고 있습니다
기본

4
@mehaase ++ 및-c에 "포인터 산술을위한 구문 설탕으로"존재하지 않는 경우, 많은 프로세서가 기본 명령어의 일부로 메모리 액세스 메커니즘 (일반적으로 포인터 인덱싱, 스택 인덱싱)을 자동으로 증가 및 감소시키기 때문에 존재합니다. 세트. 예를 들어, 6809 어셈블러에서 : sta x++... 결과를 나타내는 원자 명령어는 a어큐뮬레이터 x를 가리키는 위치에 저장 한 다음 어큐뮬레이터 x의 크기에 따라 증가 합니다. 이것은 포인터 산술보다 빠르기 때문에 매우 일반적이며 이해하기 쉽기 때문에 수행됩니다. 사전 및 사후.
fyngyrz

답변:


1059

++연산자가 아닙니다. 두 +연산자입니다. +운영자는이다 신원 아무것도하지 않는다 연산자. (설명 : +-단항 연산자는 숫자로만 작동하지만 가상 ++연산자가 문자열에서 작동 하지 않을 것이라고 가정 합니다.)

++count

다음과 같이 파싱

+(+count)

어느 것으로 번역

count

+=원하는 작업을 수행 하려면 약간 더 긴 연산자 를 사용해야합니다 .

count += 1

나는 의심 ++하고 --운영자는 일관성과 단순성을 위해 밖으로 남았다. 귀도 반 로섬 (Guido van Rossum)이이 결정에 대해했던 정확한 주장을 모르지만 몇 가지 주장을 상상할 수 있습니다.

  • 더 간단한 파싱. 기술적으로, 구문 분석 ++count이 될 수 있기 때문에, 모호 +, +, count(이 개 단항 +그냥 쉽게 될 수 있기 때문에 사업자) ++, count(하나의 단항 ++연산자). 중요한 구문 모호성은 아니지만 존재합니다.
  • 더 간단한 언어. ++의 동의어 일뿐입니다 += 1. C 컴파일러는 어리 석고 대부분의 컴퓨터 a += 1에서 inc사용 되는 명령어 를 최적화하는 방법을 몰랐기 때문에 개발 된 것입니다. 오늘날 컴파일러와 바이트 코드 해석 언어를 최적화하는 과정에서 프로그래머가 코드를 최적화 할 수 있도록 언어에 연산자를 추가하는 것은 일반적으로 특히 일관성 있고 읽기 쉬운 Python과 같은 언어에서 눈살을 찌푸립니다.
  • 혼란스러운 부작용. ++연산자가 있는 언어의 일반적인 초보자 오류 중 하나 는 증감 전 / 후 연산자 사이의 차이 (우선 순위와 반환 값 모두)를 혼합하는 것이며, 파이썬은 언어 "gotcha"를 제거하는 것을 좋아합니다. 우선 순위 문제C에서 사전 / 사후 증가는 엉망에 놀라 울 정도로 쉽게 꽤 털이합니다.

13
"+ 연산자는"identity "연산자이며 아무 것도 수행하지 않습니다." 숫자 유형에만 해당합니다. 다른 유형의 경우 기본적으로 오류입니다.
newacct

45
또한 Python에서 + = 및 friends는 표현식에 사용할 수있는 연산자가 아닙니다. 오히려 파이썬에서는 "증강 된 할당 문"의 일부로 정의됩니다. 이것은 C 언어에서 할 수있는 것과는 달리 임의의 표현식 내에서 연산자로 할당 ( "=")을 허용하지 않기위한 파이썬의 언어 디자인 결정과 일치합니다. docs.python.org/reference/…
Ned Deily

15
단항 +연산자는 사용합니다. 10 진수. 소수점 개체의 경우 현재 정밀도로 반올림합니다.
u0b34a0f6ae

21
파서 단순화에 베팅하고 있습니다. PEP 3099 의 "Python 3000에서 변경되지 않는 것" 항목에 유의 하십시오. "파서가 LL (1)보다 복잡하지 않습니다. 단순보다 복잡합니다.이 아이디어는 파서까지 확장됩니다. Python의 문법을 LL (1) 파서는 저주가 아니라 축복이다. 이것은 Perl과 같이 이름이없는 다른 역동적 인 언어와 같이 펑키 한 문법 규칙으로 끝나는 것을 막는 수갑을 우리에게 준다. " 나는 명확하게하는 방법을 볼 수 없습니다 + +++차단 LL (1)하지 않고.
Mike DeSimone

7
이 말 ++의 동의어에 지나지 않습니다 += 1. ++의 사전 증분 및 후 증분 변형이 있으므로 분명히 동일하지 않습니다. 그래도 나머지 부분에 동의합니다.
PhilHibbs

384

증가 또는 감소를 원할 때 일반적으로 정수에서이를 수행하려고합니다. 이렇게 :

b++

그러나 파이썬에서 정수는 불변 입니다. 즉, 변경할 수 없습니다. 정수 오브젝트는 여러 이름으로 사용될 수 있기 때문입니다. 이 시도:

>>> b = 5
>>> a = 5
>>> id(a)
162334512
>>> id(b)
162334512
>>> a is b
True

위의 a와 b는 실제로 동일한 객체입니다. a를 늘리면 b도 증가합니다. 그것은 당신이 원하는 것이 아닙니다. 따라서 재 할당해야합니다. 이처럼 :

b = b + 1

또는 더 간단합니다.

b += 1

에 다시 할당 b됩니다 b+1. 그것은 증가하지 않기 때문에 증가 연산자가 아닙니다 b. 그것은 다시 할당합니다.

간단히 말해서 : 파이썬은 C가 아니고 기계 코드를 둘러싼 저수준 래퍼가 아니라 증분이 의미가 없으며 C와 같이 필요하지 않은 고급 동적 언어이기 때문에 여기서 다르게 작동합니다. 예를 들어 루프가있을 때마다 사용합니다.


75
이 예는 잘못되었습니다 (아마도 불변성과 신원을 혼동하는 것 같습니다)-255까지의 숫자에 대해 동일한 객체를 사용하는 vm 최적화로 인해 동일한 ID를 갖습니다 (또는 이와 유사한 것). 예 : 큰 숫자 : >>> a = 1231231231231 >>> b = 1231231231231 >>> id (a), id (b) (32171144, 32171168)
ionelmc

56
불변성 주장은 가짜입니다. 개념적으로 변수i++ 에 할당 i + 1하는 것을 의미 합니다 i . 에 의해 지정된 객체를 수정하지 않고 i = 5; i++에 할당 6하는 것을 의미합니다 . 즉, ! 증가시키는 것은 아닙니다 . iinti5
기계 달팽이

3
@ 기계적 달팽이 :이 경우 증분 연산자가되지 않습니다. 그리고 + = 연산자는 더 명확하고 명확하며 유연하며 어쨌든 같은 일을합니다.
Lennart Regebro

7
@LennartRegebro : C ++ 및 Java에서는 i++lvalue에서만 작동합니다. 로 지정된 객체를 늘리려면 i이 제한이 필요하지 않습니다.
기계 달팽이

4
나는이 대답이 꽤 당황 스럽습니다. ++가 + = 1의 속기 이외의 것을 의미한다고 가정하는 이유는 무엇입니까? 정확히 C에서 의미하는 바입니다 (반환 값이 사용되지 않는다고 가정). 당신은 공중에서 다른 의미를 가져간 것 같습니다.
Don Hatch

52

다른 사람들의 대답은 단순한 일을하는 한 +(즉, 숫자를 그대로두면 그대로) 정확하지만, 어떤 일이 발생했는지 설명하지 않는 한 불완전합니다.

정확히 말하면 and 로 +x평가 됩니다 .x.__pos__()++xx.__pos__().__pos__()

나는 매우 이상한 클래스 구조를 상상할 수 있습니다 (어린이, 집에서하지 마십시오!).

class ValueKeeper(object):
    def __init__(self, value): self.value = value
    def __str__(self): return str(self.value)

class A(ValueKeeper):
    def __pos__(self):
        print 'called A.__pos__'
        return B(self.value - 3)

class B(ValueKeeper):
    def __pos__(self):
        print 'called B.__pos__'
        return A(self.value + 19)

x = A(430)
print x, type(x)
print +x, type(+x)
print ++x, type(++x)
print +++x, type(+++x)

13

파이썬에는 이러한 연산자가 없지만 실제로 필요한 경우 동일한 기능을 가진 함수를 작성할 수 있습니다.

def PreIncrement(name, local={}):
    #Equivalent to ++name
    if name in local:
        local[name]+=1
        return local[name]
    globals()[name]+=1
    return globals()[name]

def PostIncrement(name, local={}):
    #Equivalent to name++
    if name in local:
        local[name]+=1
        return local[name]-1
    globals()[name]+=1
    return globals()[name]-1

용법:

x = 1
y = PreIncrement('x') #y and x are both 2
a = 1
b = PostIncrement('a') #b is 1 and a is 2

함수 내에서 로컬 변수를 변경하려면 locals ()를 두 번째 인수로 추가해야합니다. 그렇지 않으면 전역 변경을 시도합니다.

x = 1
def test():
    x = 10
    y = PreIncrement('x') #y will be 2, local x will be still 10 and global x will be changed to 2
    z = PreIncrement('x', locals()) #z will be 11, local x will be 11 and global x will be unaltered
test()

또한 이러한 기능으로 다음을 수행 할 수 있습니다.

x = 1
print(PreIncrement('x'))   #print(x+=1) is illegal!

그러나 내 의견으로는 다음과 같은 접근 방식이 훨씬 명확합니다.

x = 1
x+=1
print(x)

감소 연산자 :

def PreDecrement(name, local={}):
    #Equivalent to --name
    if name in local:
        local[name]-=1
        return local[name]
    globals()[name]-=1
    return globals()[name]

def PostDecrement(name, local={}):
    #Equivalent to name--
    if name in local:
        local[name]-=1
        return local[name]+1
    globals()[name]-=1
    return globals()[name]+1

나는 자바 스크립트를 파이썬으로 번역하는 모듈에서이 함수들을 사용했다.


참고 : 위의 도우미 함수는 로컬 함수가 클래스 함수 스택 프레임에 있으면 작동하지 않습니다. 즉, 클래스 메소드 def 내에서 호출하면 작동하지 않습니다. 'locals ()'dict는 스냅 샷이며 스택 프레임을 업데이트하지 않습니다.
Adam

11

파이썬에서는 공통 Lisp, Scheme 또는 Ruby와 같은 언어와 달리 표현식과 명령문의 구별이 엄격하게 적용됩니다.

위키 백과

따라서 이러한 연산자를 도입하면 식 / 문구 분할이 중단됩니다.

같은 이유로 당신은 쓸 수 없습니다

if x = 0:
  y = 1

그러한 구별이 유지되지 않는 다른 언어에서는 가능합니다.


흥미롭게도이 제한은 다음 릴리스 Python 3.8에서 새로운 할당 표현식 구문 (PEP-572 python.org/dev/peps/pep-0572 )에서 해제 될 것입니다. if (n := len(a)) > 10: y = n + 1예를 들어 쓸 수있을 것 입니다. 차이가 있기 때문에 그 목적을 (를위한 새로운 연산자의 도입 분명합니다 :=)
Zertrin

8

TL; DR

파이썬에는 단항 증가 / 감소 연산자 ( --/ ++)가 없습니다. 대신, 값을 증가 시키려면

a += 1

더 많은 세부 사항 및 문제점

그러나 여기서주의하십시오. C 출신이라면 파이썬에서도 다릅니다. 파이썬은 대신 사용 파이썬 C가하는 의미에서 "변수"가없는 이름오브젝트를 , 그리고 파이썬에서 int의는 불변입니다.

그래서 당신이 할 말

a = 1

파이썬에서 이것이 의미하는 것은 : int값을 갖는 유형의 객체를 생성 1하고 이름 a을 바인딩 합니다. 오브젝트 의 인스턴스 int를 갖는 값 1이름은 a 그것이 지칭한다. 참조하는 이름 a과 객체는 서로 다릅니다.

이제 말해봐

a += 1

ints는 불변 이므로 여기에서 일어나는 일은 다음과 같습니다

  1. a참조 하는 객체를 찾으십시오 ( intwith id입니다 0x559239eeb380)
  2. 객체의 값을 찾으십시오 0x559239eeb380( 1)
  3. 해당 값에 1을 더합니다 (1 + 1 = 2)
  4. 값을 가진 int 객체를 만듭니다 2(object id가 있습니다 0x559239eeb3a0)
  5. 이름 a을이 새로운 객체에 리 바인드
  6. 이제 aobject를 참조 0x559239eeb3a0하고 원래 object ( 0x559239eeb380)는 더 이상 이름으로 참조되지 않습니다 a. 원래 객체를 참조하는 다른 이름이 없으면 나중에 가비지 수집됩니다.

직접 시도해보십시오.

a = 1
print(hex(id(a)))
a += 1
print(hex(id(a)))

6

예, ++ 및-기능도 누락되었습니다. 수백만 줄의 C 코드가 내 오래된 머리에 그런 종류의 생각을 쌓아 놓고 싸우지 않고 ... 내가 구현 한 클래스는 다음과 같습니다.

pre- and post-increment, pre- and post-decrement, addition,
subtraction, multiplication, division, results assignable
as integer, printable, settable.

여기 'tis :

class counter(object):
    def __init__(self,v=0):
        self.set(v)

    def preinc(self):
        self.v += 1
        return self.v
    def predec(self):
        self.v -= 1
        return self.v

    def postinc(self):
        self.v += 1
        return self.v - 1
    def postdec(self):
        self.v -= 1
        return self.v + 1

    def __add__(self,addend):
        return self.v + addend
    def __sub__(self,subtrahend):
        return self.v - subtrahend
    def __mul__(self,multiplier):
        return self.v * multiplier
    def __div__(self,divisor):
        return self.v / divisor

    def __getitem__(self):
        return self.v

    def __str__(self):
        return str(self.v)

    def set(self,v):
        if type(v) != int:
            v = 0
        self.v = v

다음과 같이 사용할 수 있습니다.

c = counter()                          # defaults to zero
for listItem in myList:                # imaginary task
     doSomething(c.postinc(),listItem) # passes c, but becomes c+1

... 이미 c를 가지고 있다면, 당신은 이것을 할 수 있습니다 ...

c.set(11)
while c.predec() > 0:
    print c

... 또는 그냥 ...

d = counter(11)
while d.predec() > 0:
    print d

... 정수로 (재) 할당 ...

c = counter(100)
d = c + 223 # assignment as integer
c = c + 223 # re-assignment as integer
print type(c),c # <type 'int'> 323

... c를 유형 카운터로 유지하는 동안 :

c = counter(100)
c.set(c + 223)
print type(c),c # <class '__main__.counter'> 323

편집하다:

그리고이 예기치 않은 (그리고 완전히 원치 않는) 행동이 있습니다 .

c = counter(42)
s = '%s: %d' % ('Expecting 42',c) # but getting non-numeric exception
print s

... 튜플 내에서 getitem ()이 사용 된 것이 아니기 때문에 객체에 대한 참조가 형식화 함수로 전달됩니다. 한숨. 그래서:

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.v) # and getting 42.
print s

... 또는 더 자세하고 사실적으로 우리가 실제로하고 싶었던 것을 명확하게 표현하지만 실제 형태로 카운터 표시됩니다 ( c.v대신 사용) ...

c = counter(42)
s = '%s: %d' % ('Expecting 42',c.__getitem__()) # and getting 42.
print s

2

C와 같은 언어와 같이 파이썬에는 사후 / 사전 증가 / 감소 연산자가 없습니다.

우리는 볼 수 있습니다 ++또는 --여러 징후가 곱해지고, 우리가 수학에서처럼 (-1) * (-1) = (+1).

예 :

---count

다음과 같이 파싱

-(-(-count)))

어느 것으로 번역

-(+count)

-부호와 -부호의 곱셈 은+

그리고 마지막으로,

-count

1
이것은 다른 답변이 그렇지 않다는 것을 무엇이라고 말합니까?
다니엘 B.

@DanielB. 다른 답변은 내부적으로 어떤 일이 발생하는지 알려주지 않았습니다. 그리고 그들은 당신이 글을 쓸 때 어떤 일이 일어날 지 말하지 않았습니다 -----count.
Anuj

첫 번째로 받아 들여진 대답은 그렇지 않습니다. ...
Daniel B.

1
곱셈이 수행되고 있다는 언급은 없습니다. 그래서 나는 consice와 포인트 답변이 동료 사용자들에게 유용 할 것이라고 생각했습니다. 당신이 그것을 이해한다면 공격이 없습니다. 학습은 배우는 곳보다 중요합니다.
Anuj

0

Python 3.8 이상에서는 다음을 수행 할 수 있습니다.

(a:=a+1) #same as a++

이것으로 많은 생각을 할 수 있습니다.

>>> a = 0
>>> while (a:=a+1) < 5:
    print(a)


1
2
3
4

또는 더 복잡한 구문으로 무언가를 작성하려면 목표는 최적화가 아닙니다.

>>> del a
>>> while (a := (a if 'a' in locals() else 0) + 1) < 5:
    print(a)


1
2
3
4

오류없이 존재하지 않으면 0을 반환 한 다음 1로 설정합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.