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

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

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

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



답변

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

++count

다음과 같이 파싱

+(+count)

어느 것으로 번역

count

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

count += 1

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

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

답변

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

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와 같이 필요하지 않은 고급 동적 언어이기 때문에 여기서 다르게 작동합니다. 예를 들어 루프가있을 때마다 사용합니다.


답변

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

정확히 말하면 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)


답변

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

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

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


답변

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

위키 백과

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

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

if x = 0:
  y = 1

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


답변

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)))


답변

예, ++ 및-기능도 누락되었습니다. 수백만 줄의 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