🧮

4.2.3. 비트연산, 할당연산, 식별연산, in 구문

1. 비트연산

비트연산자
기호
이름
설명
and
두 피연산자의 각 자리 비트의 값이 둘다 1인 경우 해당 자리에 1을 반환합니다. 하나라도 0인 경우에는 해당 자리에 0을 반환합니다.
or
두 피연산자릐 각 자리 비트의 값이 둘다 0인 경우 해당하는 자리에 0을 반환합니다. 하나라도 1인 경우에는 해당 자리에 1을 반환합니다.
xor
두 피연산자의 각 자리 비트의 값이 같을 경우 해당하는 자리에 0을 반환합니다. 두 피연산자의 각 자리 비트의 값이 다른 경우 해당하는 자리에 1을 반환합니다.
not
피 연산자의 각 자리 비트를 뒤집습니다.
shift
오른쪽에서 0들을 이동시키면서, 왼쪽 피연산자 이진수의 각 비트를 오른쪽 피연산자 비트 만큼 왼쪽으로 이동시킨 값을 반환합니다.
shift
사라지는 비트를 버리면서, 왼쪽 피연산자 이진수의 각 비트를 오른쪽 피연산자 비트만큼 이동시킨 값을 반환합니다.
비트연산자는 실제 사용할 일이 많이 없기 때문에 당장에 어려움을 느끼신다면 이 부분은 넘어가셔도 좋습니다.
예제를 살펴보도록 하겠습니다.
입력
a = 2 b = 3 print(a & b)
 
출력
2
 
위에서 2와 3을 가지고 계산을 했습니다. 이것의 계산 과정을 살펴보겠습니다.
# a의 값 2를 2진수로 풀어줍니다. 0010 # b의 값 3을 2진수로 풀어줍니다. 0011 # 위아래로 붙여보겠습니다. 0010 0011 # & 연산의 경우 위와 아래 한짝씩 맞춰 and 비교를 해봅시다. # 이때 1 = True, 0 = False 입니다. 0010 0011 ---- 0010 # 결과값을 다시 10진수로 변환합니다. 2
 
이번에는 | 연산을 해보도록 하겠습니다.
입력
a = 7 b = 8 print(a | b)
 
출력
15
 
# a의 값 7을 2진수로 변환합니다. 0101 # b의 값 8을 2진수로 변환합니다. 1000 # 위아래로 붙여보겠습니다. 0101 1000 # 연산의 경우 위와 아래 한짝씩 맞춰 or 비교를 해봅니다. # 이때 1 = True, 0 = False 입니다. 0101 1000 ---- 1101 # 결과값을 다시 10진수로 변환해줍니다. 15
 
비트연산자의 연산의 전개 과정은 다음과 같습니다.
 
  1. 10진수를 2진수로 변환
  1. 2진수에서 비트연산을 전개
  1. 결과값을 다시 10진수로 변환
 
XOR보수연산 또한 설명 대로 전개하면 되기에 생략하도록 하겠습니다.
 
왼쪽 쉬프트 연산을 한 번 해보겠습니다. 이 때 주의할 점은 왼쪽 쉬프트 연산의 오른쪽 값은 2진수로 바꾸는 용도가 아닙니다. 그만큼 비트 이동을 하겠다는 의미입니다.
입력
a = 7 b = 2 print(a << b)
 
출력
28
# a의 값 3을 2진수로 바꿉니다. 0111 # b 만큼 이동할 것이기 때문에 b는 바꾸지 않음 # a의 값들(집합)을 왼쪽으로 b만큼 이동시키고 뒤에 0을 붙임 011100 # 결과 값을 10진수로 다시 변환합니다. 28
 
이번에는 오른쪽 쉬프트 연산을 해보도록 하겠습니다.
입력
a = 7 b = 2 print(a >> b)
 
출력
1
 
# a의 값 7을 2진수로 바꿉니다. 0111 # b만큼 이동할 것이기 때문에 b는 바꾸지 않음 # a의 값들(집합)을 오른쪽으로 b만큼 이동시키고 넘어간 것을 잘라냄 0001(11) # ()는 넘어간 것을 의미합니다. 이것들은 잘라내어 버립니다. 0001 # 결과 값을 10진수로 다시 변환합니다. 1
 
a = 40 ~a #bit NOT(2의 보수) 101000 #비트단위를 뒤짚었습니다. 010111 #비트단위를 뒤짚고 +1했습니다. 011000 101000 011000 000000 #자기 자신와 보수의 값을 더하면 값은 0이 나옵니다. #1. 0을 2개로 표현하지 않아도 됩니다. #2. 값을 더했을 경우 올림수 하나만 버리면 됩니다.

2. 할당연산

할당연산자
기호
이름
설명
예제
동일한 코드
대입
연산자의 오른쪽 값을 왼쪽 변수에 할당합니다.
a = b
a = b
덧셈 대입
연산자의 왼쪽 변수 값과 오른쪽 값을 더한 결과를 왼쪽 변수에 할당합니다.
a += b
a = a + b
뺄셈 대입
연산자의 왼쪽 변수의 값에서 오른쪽 값을 뺀 결과를 왼쪽 변수에 할당합니다.
a -= b
a = a - b
곱셈 대입
연산자의 왼쪽 변수의 값과 오른쪽 값을 곱한 결과를 왼쪽 변수에 할당합니다.
a *= b
a = a * b
제곱 대입
연산자의 왼쪽 변수의 값에서 오른쪽 값만큼 제곱한 결과를 왼쪽 변수에 할당합니다.
a **= b
a = a ** b
나눗셈 대입
연산자의 왼쪽 변수의 값을 오른쪽 값만큼 나눈 결과를 왼쪽 변수에 할당합니다.
a /= b
a = a / b
몫 대입
연산자의 왼쪽 변수의 값을 오른쪽 값만큼 나눈 몫을 왼쪽 변수에 할당합니다.
a //= b
a = a // b
나머지 연산 대입
연산자의 왼쪽 변수의 값을 오른쪽 값만큼 나눈 나머지를 왼쪽 변수에 할당합니다.
a %= b
a = a % b
대입 연산자는 산술 연산의 코드를 축약해서 사용할 수 있도록 지원하는 기능입니다. 피연산자를 한번만 사용하기 때문에 대입 연산자를 잘 활용한다면 코드를 좀 더 간결하게 사용할 수 있다는 장점이 있습니다.
처음 사용해서 익숙하지 않고, 혼란의 여지가 있을 경우에는 사칙 연산자인 형태로 사용하시는 것을 권해드립니다.
하지만 곧 간결한 코드를 중요하게 여겨지는 순간이 올테니 천천히 할당 연산자를 활용하는 훈련을 조금씩 해주세요.
물론 함께 일하는 팀의 컨벤션 등이 정해진다면 그것에 따르는 것이 좋습니다.
입력
#'연산자' + '=' numA = 10 numB = 20 numA += numB print("numA:", numA) print("numB:", numB)
 
출력
# numA와 numB가 더해져서 numA는 30이 됩니다. numA: 30 # numB는 변화가 없습니다. numB: 20
 

2.1 연산자 우선순위

  1. 리스트 , 튜플, 딕셔너리, 셋
  1. 인덱싱, 슬라이싱
  1. **
  1. *, @, /, //, %
  1. +, -
  1. 비트단위 연산
  1. in, not in, is, is not
  1. 논리연산(not, and, or)
  1. if, else
  1. lambda
위의 순위 순서대로 연산을 하시면 됩니다.

3. 식별연산

식별 연산자
기호
설명
피연산자들의 객체 메모리의 위치(주소)가 같다면 참(True)을 반환합니다. 그렇지 않으면 거짓(False)을 반환합니다.
피연산자들의 객체 메모리의 위치(주소)가 다르다면 참(True)을 반환합니다. 그렇지 않으면 거짓(False)을 반환합니다.
식별 연산자는 해당 값이 들어있는 값이 들어있는 주소를 비교하는 연산자입니다. C, C++ 언어를 학습하신 분이라면 포인터로 이해하시면 됩니다.
 
그렇지 않으신 분을 위해 간단히 설명하자면, 변수를 선언해서 값을 저장할 때 그 값을 저장하는 공간이 필요한데 그 공간의 위치를 주소라고 합니다. 저희들이 사용하는 주소와 비슷한 개념이라고 보시면 됩니다. 서울시 OO구 OO동 같은 주소 체계를 컴퓨터에서도 가지고 있는 것입니다.
 
coffee 라는 회사에서 일하고 있는 Brand 라는 사람이 있다고 가정합시다.
orange 라는 회사에서 일하고 있는 Brand 라는 사람이 있는 이 둘이 동일 인물인지 확인해봅시다.
입력
coffee = "Brand" orange = "Brand" print(coffee is orange) print(coffee == orange)
 
출력
True True
 
같다면 동일인물이라는 것을 알 수 있습니다.
즉 이 두 회사에 다니고 있는 Brand 라는 이름을 가진 사람은 서울시 OO구 OO동 XX번지에서 살고 있는 동일인물이라고 생각하면 됩니다. (물론 실제로 컴퓨터 내에서의 주소는 이런 주소가 아닌 0012FF64 와 같은 16진수 주소 체계를 가집니다.)
 
이때 그럼 ==와 차이가 뭔지 궁금해할 수 있습니다. == 비교 연산자는 값이 같은지 찾는 것입니다.
위의 예시로 보자면 같은 Brand 라는 이름을 가졌는지를 보는 것입니다.
다음 예시를 보면서 확인해 보겠습니다.
입력
a = [1, 2, 3] b = [1, 2, 3] c = a print('a == b', a == b) print('a == c', a == c) print('a is b', a is b) print('a is c', a is c)
 
출력
a == b True a == c True a is b False a is c True
 
위에서는 Brand 라는 문자열을 예시로 들었습니다. 파이썬의 경우 동일한 문자열이 있다면 해당 문자열을 가진 주소를 그대로 참조하게 되어있습니다. 그렇기 때문에 동일한 문자열을 생성하는 경우에는 동일한 주소를 보고 있게 됩니다. (항상 그런 것은 아닙니다.)
 
배열과 같은 객체는 문자열과 달리 새로 생성할 때 새로운 주소에 담습니다. 따라서 값은 동일하더라도 다른 주소를 가지게 될 수 있습니다.
 
따라서 위의 예시와 같이 a, b, c의 값을 비교하는 것은 True로 나오지만 주소를 비교했을 때는 다르게 나오는 것을 볼 수 있습니다.
입력
#in 구문 a=[1, 2, 3, 4] b=[1, 2, 3, 4] a==b, a is b, a in b
 
출력
(True, False, False)
 
입력
a=[1, 2, 3, 4] b=[1, 2, 3, 4] 1 in b [2] in b [1, 2] in b
 
출력
(True, False, False)
 
입력
'l' in 'leehojun' 'lee' in 'leehojun'
 
출력
True True
 
입력
10 in (10, 20, 30), [2] in (10, 20, 30), [1, 2] in (10, 20, 30) #튜플 {10, 10, 10} #셋 {'key':'value', 'key2':'value'} #딕셔너리 a={10, 10, 20} 10 in a, {10} in a, {10, 20} in a a={'key':'value', 'key2':'value'} 'key' in a 'value' in a ('key', 'key2') in a {'key', 'key2'} in a
 
출력
(True, False, False) (True, False, False) True False False ERROR