🚦Summary

  • “&” 과 “and”는 모두 표현식 이며, 비교연산에 사용될 수 는 있지만, 그 용도와 작동 방식이 다릅니다.
  • 같은 ‘and’라는 단어적 의미로 이해를 하고 구분 없이 사용하면 올바른 코드를 작성했음에도 불구하고 잘못된 결과를 도출하는 문제가 발생할 수 있습니다.
  • 그 원인은 바로 연산우선순위와, 비트연산이라는 차이에서 기인하며, 이로 인해 예상과는 다른 연산을 하게 됩니다.
  • 따라서 각각의 특성을 고려해 목적에 맞게 사용하는 것이 중요합니다.
  • 다만, 조건문(Conditional Statement) 에서는 반드시 ‘and’ 연산자를 사용해야 예상치 못한 연산오류 없이 원하는 결과를 얻을 수 있습니다.


📌 Intro.

  • 저는 현재 부트캠프에 참여하여 AI에 대해 배우는 중입니다. 최근 python의 기초개념에 대해 배우고 있는데, 여러 문제를 푸는 과정에서 ‘and’ 연산자로 조건식을 썼을 때와 ‘&’ 연산자로 조건식을 썼을 때, 기대와는 다르게 결과에 차이가 있었는데, 그 원인을 질문하는 동기 그루분의 질문이 있었습니다. (이 문제에 대한 내용은 거북이 미로찾기 문제에서 나온 것이었습니다. 👉 ‘거북이 미로찾기’ 클릭 시 해당 문제 풀이로 이어집니다.)
  • 저도 당연히 둘 다 말그대로 ‘and’ 조건으로 쓰는 표현식이라 생각을 했고, 둘의 차이가 뭐길래 결과값이 달라지는 경우가 생기는지 궁금했습니다.
  • 질문에 대한 답은 어느정도 들었지만, 사실 잘 이해가 가지 않았습니다. 그래서 하나 하나 찾아보고 정리를 해보려고 합니다.


Python에서 ‘and’와 ‘&’ 의 차이점 이해하기

  • Python 프로그래밍 언어에서 ‘and’와 ‘&’는 모두 표현식에 사용되지만, 두 연산자 사이에는 근본적인 차이가 있습니다. ‘and’는 두 표현식이 논리적으로 참인지 테스트하는 ‘논리 연산자’ 입니다. 반면 ‘&’ 연산자는 ‘비트 단위 연산자’ 로, 비트에 대해 작동하며 비트 단위의 연산을 수행합니다.

  • Python에서는 비어있는 내장 객체들이 논리적으로 False로 처리됩니다. 반면에 비어있지 않은 내장 객체들은 논리적으로 True로 간주됩니다.

    • 좀 더 풀어서 말하자면 빈 리스트(‘[ ]’), 빈 딕셔너리( ‘{ }’ ), 빈 세트 ( ‘set()’ ) 등은 모두 False라 판단합니다.
    • 반면에 이 리스트, 딕셔너리, 세트에 어떠한 유형이든 ( ‘’ 로 표현되는 빈문자열이 있다고 해도) 값이 채워져 있다면, True라 판단합니다.
    • 이에 대한 다양한 예제를 코드로 보자면 아래와 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 빈 리스트, 튜플, 딕셔너리, 세트
print(bool([]))  # False
print(bool(()))  # False
print(bool({}))  # False
print(bool(set()))  # False

# 비어 있지 않은 리스트, 튜플, 딕셔너리, 세트
print(bool([1]))  # True
print(bool((1,)))  # True
print(bool({'key': 'value'}))  # True
print(bool(set([1])))  # True

# 빈 문자열과 비어 있지 않은 문자열
print(bool(''))  # False
print(bool('text'))  # True

# 숫자 0과 0이 아닌 숫자
print(bool(0))  # False
print(bool(1))  # True
print(bool(-1))  # True
print(bool(0.1))  # True
  • 이처럼 객체에 대해 python이 True인지 False인지 판단 하는 기준이 있는 것은 사용자로 하여금 다양한 연산과 비교의 작업을 더 수월하게 할 수 있도록 합니다.

  • 예를들어 이러한 특성을 사용해 단순히 값이 True이다 False이다를 판단하는 것 뿐만 아니라 이를 응용해 조건문에 쓰일 수도 있고, 특정 코드의 실행을 위한 Trigger 가 될 수 도 있습니다.

  • 일반적으로 알려져 있는 ‘and’와 ‘&’의 차이점을 표로 정리하면 아래와 같습니다.

    • 정수값이 0일 경우 파이썬에서는 False로 간주합니다. 하지만 논리적으로 사용될때는 True로 간주됩니다. 이것은 ‘and’가 두 표현식이 논리적으로 참인지 거짓인지를 테스트 하는 반면, ‘&’는 두 문장의 결과에 대한 비트별 and 연산을 수행하기 때문입니다.
파라미터 ‘and’연산자 in python ‘&’연산자 in python
기본 두 피연산자가 모두 참일때 참을 반환 비트 단위 연산을 수행
True 표현식 두 표현식이 논리적(logically)으로 ‘True’ 로 쓰였는지를 판단 T/F value들과 함께 쓰일 때, 두개의 value가 모두 True인지 테스트
  • 아직은 좀 어려운 것 같습니다. 결국 “둘 다 ‘비교’ 할때 쓰는거고 둘다 ‘True’인지 ‘False’인지 판단할 때 쓸 수 있다는 것 같은데, 그냥 판단을 할 때 사용하는 값과, 방법이 다르다는거 아닌가?!” 라는 생각이 들 뿐입니다.

  • 좀 더 각각의 개념을 뜯어볼 필요가 있어 보입니다. 파이썬에서 말하는 ‘논리적(Logical)’이란 것의 정의는 무엇이며, 비트연산이란건 또 무엇인지 이것들을 알아야 이해가 될 것 같습니다.

  • 일단은 python에서 정의하는 ‘and’와 ‘&’ 가 무엇인지부터 좀 더 상세히 알아보면 좋을 것 같습니다.



Python에서 “and” 란 무엇인가?!

  • ‘and’ 는 두 피연산자(and 앞뒤에 있는 값이나 표현식, 조건 등)가 모두 True일때 True를 반환(데이터 타입이 bool)하는 논리적 And 연산자로서 논리연산자(Comparison Operators)의 하나 입니다.
  • 여기서 말하는 ‘논리적인(Logically)’ 란 말은 Boolean 값이나 조건문에서 주로 사용되는 개념인데, 어떤 값이 True인지 False인지를 나타냅니다.
  • ‘python에서 모든 값은 기본적으로 True 또는 False 이다.’ 라는 전제가 있기 때문에 이 개념의 적용이 가능합니다. 이를 파이썬에서는 trueiness(논리적 진리성)이라 하며, 위에서 살펴본 비어있는 리스트, 딕셔너리, 세트와 값이 있는 것들간의 비교가 성립하는 이유이기도 합니다.


Python에서 “&” 란 무엇인가?!

  • ‘&’는 Python에서 사용되는 비트 연산자로, 정수의 이진수 표현에서 각 비트별로 ‘and’ 연산을 수행하여 그 결과를 return합니다.
  • 즉, 두 정수의 이진수에 대해서 해당 비트의 값이 모두 ‘1’일때만 그 비트를 ‘1’ 이라고 return합니다.
    • 예를들어, 두 정수 5(이진수로는 ‘101’)와 3(이진수로는 ‘011’)에 대해 ‘&’ 연산을 하면, 각 비트를 비교하여 return합니다.
      • 즉, 101 & 011 로 비교를 하며, 그 결과는 001 이 되어 ‘1’ 이라는 비트값을 return합니다.
      • 만약 결과값이 ‘0’ 이면 ‘0’ 이라는 비트값을 return합니다.
  • 예를 들어, 두 정수의 비트를 각각 비교해서 두 비트의 값이 모두 ‘1’ 이면 해당 비트의 값은 ‘1’ 이라 return합니다. 이때 파이썬에서 개념적으로 1은 True이기때문에 이 경우를 True로 판단합니다.
  • 반대로, 두 비트 값이 모두 ‘1’ 이 아니면 비트의 값은 ‘0’ 이라 return합니다. 그리고 파이썬에서 숫자 ‘0’ 은 False를 의미합니다.
이진수의 ''&" 연산 살펴보기 🔍
  • 5와 3의 이진수 연산 예시
    • 5의 이진수는 101
    • 3의 이진수는 011
  • 비트별 AND 연산
    • 이진수의 각 자리(비트) 별로 비교
    • 첫번째 비트 : 1 & 0 == 0
    • 두번째 비트 : 0 & 1 == 0
    • 세번째 비트 : 1 & 1 == 1
  • 비트별 and 연산 결과는 001 이며 이를 십진수로 하면 1이 됩니다.
  • 따라서 5 & 3 연산의 결과는 int 인 ‘1’ 을 return하게 되고, 이것이 조건문에서는 True로 인식이 됩니다.


그래서 둘의 차이란게 뭔데…😥

  • 돌고 돌아 비슷한 말들이 반복되지만 결국 핵심은 “둘다 ‘A’ 와 ‘B’ 를 비교하는데, 그 비교에 사용하는 값의 type이 다르다.” 는 것입니다.
  • ‘and’ 연산자는 값 자체가 가지고 있는 논리적 진리성에 따라 A와 B를 비교해 True와 False를 판단하고 ‘&’ 연산자는 A의 비트 연산결과와 B의 비트연산결과를 ‘and’ 연산으로 한번 더 비교해서 True와 False를 판단하는 것입니다.
  • 좀 더 확실한 이해를 위해 아래의 예제 코드를 살펴보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
# and 연산자 예시
a = 4
b = 0
result = a and b  # 여기서 b가 0이므로, 결과는 0이 됩니다.

# & 연산자 예시
a = 4  # 이진수로 100
b = 5  # 이진수로 101
result = a & b  # 이진수로 100 & 101 = 100, 결과는 4가 됩니다.

  • 이제 좀 어떤 차이인지 알 것 같습니다. 하지만 여전히 좀 이해가 안가는게 있습니다. 제가 배우면서 봤던 문제풀이에서의 이슈는 단순히 숫자의 비교가 아니라 다양한 조건식이 포함된 경우 였습니다.

  • 그래서 이런 생각이 들 수 밖에 없었습니다. “이건 단순히 숫자연산인 경우이고… 다양한 변수간의 비교, 값에 대한 인덱싱 같은게 들어가는건 이렇게 이진화 할 수가 없는거 아닌가?”

  • 맞습니다. ‘&’ 연산자는 결국 숫자의 비트 연산에 사용되는데, 숫자 이외의 형태인 데이터(문자열, 리스트, 딕셔너리 등)에는 ‘직접적으로 적용’ 되지는 않습니다.


👉 파이썬에서 다양한 연산자의 연산 순서를 알면 이해가 될 수 있습니다.

  • ‘&’ 연산자는 숫자에 대해 적용되는데, 실제로 ‘&’ 연산자를 이런 데이터들에게 사용하면 작동이 잘만 됩니다. 왜일까요..?!

  • 이는 ‘&’ 를 사용하기 위한 사전 연산 단계가 거쳐지기 때문입니다. 보통 우리가 조건문, 비교 표현식 등을 작성하면 단순히 변수 두개를 놓고 ‘A & B’ 형태로 쓰기도 하지만, A[x] <= vx < 5 & B[y] >= 3 과 같은 식으로 다양한 변수에서 특정 값에 접근하는 형태로 사용하기도 합니다.

  • 이 경우 각 부분의 연산자로 인한 비교 결과(True or False)도출 한 뒤 그 값을 ‘이진수’ 로 변경하여 ‘&’ 연산을 사용합니다.

  • 이러한 복합식에서의 ‘&’ 연산자에 대해 좀 더 톺아보기를 하면 좋을것 같습니다.

  • 처음 제가 의문을 가지게 된 거북이 미로찾기 의 조건문을 가지고 비교해 보겠습니다.
    • 제가 작성했던 코드는 if 0 <= nx < 5 and 0 <= ny < 5 and maze[nx][ny] == 0: 였는데
    • 여기서 ‘and’를 ‘&’ 로 바꾸문 조건식 은 다음과 같습니다.
      • if 0 <= nx < 5 & 0 <= ny < 5 & maze[nx][ny] == 0:
      • 여기서 nx와 ny는 2D 배열의 리스트(maze)에서 거북이가 이동할 좌표값입니다.
      • 거북이가 있는 미로는 (4,4) 배열의 2D이기 때문에 0~4 사이의 미로 범위에서만 움직여야 하므로 이를 조건문으로 표현한 것입니다.
    • 실제 로직에서는 막히거나(숫자1) 지나온길(숫자2) 여서 갈 수 없는 길인데 ‘&’ 조건문을 쓰면 거북이가 이동하는 현상이 발생합니다.
  • 이제 파이썬에서 다양한 연산자들이 어떤 우선순위를 가지고 작동하는지를 살펴 보겠습니다.


파이썬에서의 연산자 작동순서


💡결론. 거북이 미로찾기로 본 조건문에서 ‘&’ 사용을 조심해야 하는 이유!

  • 여기에는 크게 2가지 이유가 있습니다. ‘&’ 의 연산결과가 비트단위라는 것, 그리고 연산의 우선순위가 ‘and’ 조건보다 앞이라는 것 입니다.
  1. 비트 단위 연산`
    • ‘&’ 연산자는 연산 대상들의 비트를 대상으로 ‘and’ 연산을 수행합니다. 이는 일반적인 논리연산자인 ‘and’와는 작동방식이 다릅니다.
    • 보통 ‘and’ 는 표현식 전체의 논리적 True와 False를 평가합니다.
    • 하지만 ‘&’ 는 연산대상의 ‘비트값’ 에 따라 결과를 결정 합니다.
    • 특히 그 연산 대상이 ‘숫자’ 일때 예상치 못한 결과를 도출 할 수 있습니다.
  2. 연산 우선순위
    • 위에서 정리했듯, 파이썬에서 ‘&’는 ‘and’ 연산자보다 높은 연산 우선순위를 갖습니다.
    • 이는 표현식내에서 ‘&’가 다른 연산자와의 우선순위에서 적용되는 관계가 ‘and’ 와는 다르다는 것을 의미합니다.


  • 위의 연산 순서를 실제 거북이 미로찾기에서 if 0 <= nx < 5 & 0 <= ny < 5 & maze[nx][ny] == 0:에 적용하면 아래와 같은 순서로 풀이가 됩니다.

  • 1단계 : 비교연산자 우선순위 평가
    • ‘&’ 연산자가 비교연산자 보다 우선순위가 높으므로 먼저 평가를 진행합니다.
    • nx < 5 & 0과 같은 부분이 먼저 계산됩니다. 이는 nx < 5의 결과(비트 값으로 변환된)와 0의 비트 값을 AND 연산하는 것을 의미합니다.
    • 이 과정이 0 <= nx0 <= ny < 5의 각 부분에서 진행됩니다.

  • 2단계 : 비교연산자 평가
  • 이제 0 <= nx0 <= ny가 평가됩니다. 이는 단순 비교 연산으로, 각각의 값이 주어진 범위 내에 있는지 확인합니다.

  • 3단계 : 마지막 조건 평가
    • maze[nx][ny] == 0이 평가됩니다. 이는 nxny가 가리키는 maze 배열의 위치가 이동 가능한지(값이 0인지) 확인합니다.
    • maze는 2차원 배열이며, 이 배열의 각 요소는 미로의 특정 위치를 나타냅니다.
    • 여기서 0은 “이동 가능한 위치”를 의미합니다.


  • 4단계 : 최종결과 도출
  • 1~3단계를 거쳐 모든 조건이 True인 경우, if 문 내부의 코드들이 실행되고, 그렇지 않은 경우 if 문을 건너뜁니다.


  • 반면에 ‘and’ 조건으로 작성한 if 0 <= nx < 5 and 0 <= ny < 5 and maze[nx][ny] == 0: 를 사용했다면 0 <= nx < 50 <= ny < 5 가 각각 연산된 뒤에 두 값을 비교해 True/False를 비교한뒤, 그 결과와 maze[nx][ny] 의 결과를 다시 and 연산으로 비교해서 True와 False를 판단 합니다.

  • 즉, 단순히 ‘&’와 ‘and’ 의 차이지만 연산을 하는 순서가 달라 예상치 못한 오류가 발생할 수 있습니다.

  • 따라서 조건문(Conditional Statement) 에서는 반드시 ‘and’ 연산자를 사용해야 예상치 못한 연산오류 없이 원하는 결과를 얻을 수 있습니다.


🫡 Outro.

  • 이번 포스팅에서는 ‘&’ 연산자와 ‘and’ 연산자가 조건문에서 어떻게 작용하는지, 왜 연산의 결과가 다른지 그 원인을 공부하여 정리했습니다.
  • 단순한 질문에서 시작한 것이 의외로 심도 깊은 내용이어서 자료를 찾고 공부하는데 상당히 오래 걸리게 되었습니다.
  • 그럼에도 흔히 할 수 있는 실수인 ‘&’ 연산자의 사용에 좀 더 경각심을 가질 수 있게 될 것 같고, 코드 작성에서 이런 부분을 고려해서 작성하게 될 수 있는 계기가 되어 오히려 다행이라 생각합니다.


Reference

댓글남기기