이 앞전에서는 프로그래밍 언어로써 번역 방식에 따른 차이인 컴파일·인터프리터 언어의 차이, 파이썬의 동작 원리에 대해 알아보는 시간을 가졌습니다. 아마 가벼운 마음으로 글을 읽고 있던 분들에겐 상당한 부담으로 이어질 수 있었겠습니다만 그래도 중요한 내용이니 제대로 숙지를 하시고 오시는걸 추천합니다.
뭐.. 열심히 읽었는데도 완벽히 이해가 안되면 그냥 넘어가자구요. 저희 강의 목적이 전문가가 되자는건 아니니깐요 ㅎㅎ 계속 계속 공부하다보면 언젠가 깨닫는 시점이 오게 됩니다.
오늘은 프로그래밍 언어에 있어 아주 중요한 부분. 바로 변수에 대해 알아보는 날입니다.
변수란?
print("Hello World")
print(2 ** 10)
print(1 + 1)
우리는 앞에서 print 라는 것을 사용하여 컴퓨터 모니터에 글자를 출력할 수 있다는 것을 배웠습니다. print 안에는 그냥 우리가 일상 생활에 사용하는 "Hello World" 라는 문자열도 가능하고, 2 ** 10, 1+1 같은 숫자 계산식도 출력이 가능했죠.
print(1 + 1)
print(2 ** 10)
그런데 위와 같은 코드를 보면서 A씨는 다음과 같은 생각을 가집니다.
??? : 음.. 1+1 을 계산해서 출력시키니 2가 나오고 2 ** 10을 계산해서 출력시키니 제대로 된 결과값이 나오는건 좋아. 근데 나중에 계산을 위해 이걸 어딘가에 저장해두고 싶은데.. 어떻게 해야할까?
이러한 생각이 바로 오늘의 핵심 주제인 변수를 관통하는 생각입니다. 위와 같이 print() 로 바로 바로 결과를 출력하는건 좋습니다만, 이러한 것만으로는 데이터를 어딘가에 저장해둘수가 없습니다. 2 ** 10 을 바로 출력할게 아니라 어딘가에 저장해놨다가, 나중에 필요할 때 출력하고 싶을때가 있을탠대 말이죠.
하지만 변수를 사용하면 저렇게 실행중 원하는 데이터를 저장할 수 있습니다. 무려 이름을 붙여서요.
print(2 ** 10)
>>> 1024
해당 코드와
a = 2 ** 10
print(a)
>>> 1024
해당 코드는 출력 결과는 동일합니다.
a = 2 ** 10
그런데 처음 보는 특이한 코드가 있습니다.
이 코드는 바로 a라는 공간에 2 ** 10 의 결과를 저장하라는 명령입니다.
여기서 a가 바로 오늘 배우는 변수입니다.
조금 일상생활의 예를 들어보자면 a라는 카트에 1024라는 값을 담아놓은 상태라고 이해할 수 있습니다.
print(a)
이후 a의 값을 출력하라고 명령했습니다. 현재 a라는 공간(카트) 엔 2 ** 10 의 계산 결과인 1024가 저장되어 있으므로 1024가 출력됩니다.
이제 a가 1024에 저장되어 있는게 확실하므로 앞으로는 2 ** 10을 코드 상에서 여러번 사용해야 하는 상황이라면 2 ** 10을 매번 써줄게 아니라 그냥 a라는 단어 하나만 써주면 됩니다. 간편해졌죠?
a = 2 ** 10 의 의미를 조금 더 깊이 이해해봅시다.
아마 "=" 기호를 수학에서의 것과 비슷하게 이해하신 분들이 많을겁니다.
하지만 엄밀히는 수학에서 사용하는 등호 "=" 와는 거의 다른.. 아니 전혀 다른 의미라고 생각해야 합니다.
사실 a = 2 ** 10 에서 사용하는 "=" 는 등호가 아니라 대입연산자라고 합니다.
대입을 수행하는 일종의 연산 기호라는 뜻입니다.
저 대입 연산자의 의미는 한마디로 자신을 기준으로 오른쪽에 있는 값을 왼쪽에 대입해라 (덮어써라) ! 라는 의미입니다.
절대로 a 와 2 ** 10 이 같다는 의미가 아닙니다.
일단은 대입하면 물론 같아지기야 하겠지만 정확히는 오른쪽에 있는 값을 왼쪽으로 쓰는 명령입니다.
따라서 2 ** 10의 결과인 1024를 a에 쓴다! 가 되는겁니다.
항상 이렇게 해석하는 습관을 들이셔야 합니다.
a = 2 ** 10
a = a + 1
print(a)
>>> 1025
이제 다른 코드를 한 번 봐보겠습니다.
우선 이 코드의 출력 결과는 1025입니다.
한 줄 한 줄씩 이 코드의 동작 원리를 살펴봅시다.
이전에 언급드린대로 거의 99%.. 아니 99.9%의 프로그래밍 언어는 코드를 위에서 아래로 한 줄씩 실행합니다. 특히 파이썬의 경우에는 인터프리터를 활용하는 언어 특징상 한 줄씩 실행하는게 더 두드러집니다.
먼저 첫번째 줄이 실행됩니다. 대입 연산자 "=" 에 의해 오른쪽의 값 2 ** 10을 왼쪽 a라는 공간에 저장합니다.
공간 a가 만들어지며 1024 값이 저장되게 됩니다.
다음으로 프로그래밍을 처음 배우신 분들이라면 매우 이상하게 보이는 식인 a = a + 1 입니다. 앞에서 언급드렸다 싶이 =은 수학에서 같다는 뜻(등호)이 아닙니다. a와 a + 1이 수학적으로 같은건 절대 아니죠?
아까도 말했다 싶이 = 은 대입 연산자라고 부르며 자신을 기준으로 오른쪽에 있는 값을 왼쪽에 대입합니다.
결론적으로 오른쪽의 값 a + 1을 a에 대입하는 명령어 입니다.
기존에 a의 값이 1024 였으므로 a + 1은 1024 + 1 으로 해석되며 1025가 a에 다시 쓰여지게(대입) 됩니다.
그래서 a = a + 1을 실행한 순간 a는 1025가 됩니다.
a = a + 1 과 같은 문구는 기존 변수가 가진 값에 누적해서 1을 더하는 형태의 문구로 굉장히 자주 사용되는 코드입니다. 꼭 기억해두세요.
C, Java와 같은 언어에서는 a = a + 1 과 같은 코드를 많이 사용하게 되므로 a = a + 1을 a++로 줄여서 사용할 수 있게 해줍니다. 즉 a++ 와 a = a + 1 은 동일한 기능을 하는 코드가 되죠. 하지만 파이썬에서는 a++와 같은 표현을 지원하지 않으니 주의해야 합니다. a = a + 1과 같이 작성해야 합니다.
다만 다른 언어에서 a++을 만나면 이건 a = a + 1 과 동일한 구문이구나! 하고 이해하시면 됩니다.
마지막으로 a를 출력합니다. 1025가 출력됩니다.
변수는 어디에 저장될까?
저번에도 배웠지만 저희가 HDD/SSD 에 저장된 프로그램을 실행하면 RAM위에 올라와 실행되며, 이를 프로세스라고 하고 CPU에 의해 코드가 읽혀 실행된다고 배웠습니다.
모든 프로그램은 RAM위에서 실행된다는 특성 때문에, 모든 실행중인 프로그램의 데이터, 심지어 그 프로그램의 코드까지도 RAM위에 저장되게 됩니다.
이것이 컴퓨터를 배우면서 알아야 할 첫 번째 진리입니다. 모든 프로세스 (실행중인 프로그램) 의 데이터는 RAM에 저장됩니다. 컴퓨터를 처음 설계한 사람이 구조를 이렇게 만들어 놨습니다. 받아들이면 됩니다.
a = 2 ** 10
따라서 우리가 지금 실행중에 저장하는 a라는 공간의 값 1024 역시 RAM에 저장되고 있습니다. 심지어 코드까지요.
결론적으로 우리가 별도로 파일로 저장하지 않는 이상 코드던 변수던 전부 RAM에 저장되고 있습니다.
코드가 RAM에 저장되어 실행되는건 그렇게 중요한 내용은 아닐 수 있는데 어쨌던 변수가 RAM에 저장되고 있다는 사실은 확실합니다.
보통 RAM을 흔히 메모리라고 부릅니다. 따라서 본 글에서 메모리라고 부르는 것은 RAM을 칭하는거라고 이해하시면 됩니다. RAM이라고 부르던 메모리라고 부르던 섞어불러도 똑같은 것을 지칭하는 겁니다.
따라서 변수를 제대로 정의해보자면
쉽게는 값을 담을 수 있는 이름이 있는 상자! 가 되겠고
조금 엄밀하게는 데이터를 저장하고 계속해서 편리하게 사용할 수 있게 해주는 메모리 공간이다.
가 되겠습니다.
어떻게 이해하시던 본인이 편한대로 이해하시면 됩니다.
카트던, 값을 저장하는 메모리 공간이던, 값을 담는 그릇이던..
이름이 변수(변하는 수)인 이유도 공간을 하나 잡고 내가 원하는대로 값을 계속 써서, 저장하고 값을 변경시킬 수 있기 때문입니다.
누적곱,
변수에 대해 더 자세히 알아보기
a = 10
a = 3.14
a = "Hello World"
print(a)
>>> Hello World
파이썬에서는 아까도 봤듯이 변수를 만들려면 변수 명 = 값 의 형태로 만들어주면 변수 공간이 잡히며 그 공간에 값이 대입되게 됩니다.
같은 변수 이름으로 계속 대입을 진행하면 덮어쓰기가 진행되어서 맨 마지막에 대입 된 것이 그 변수의 값이 되게 됩니다.
그에 관한 예제가 위 코드입니다.
첫 번째 라인이 실행됩니다. 10의 값을 a라는 변수(메모리 공간) 에 담습니다.
a라는 공간이 잡히며 10이 저장됩니다.
다시 a라는 공간에 3.14를 대입합니다. 이미 있는 공간에 다시 대입하면 덮어쓰기가 진행됩니다. 10은 사라지고 3.14가 남게 됩니다.
마지막으로 a에 Hello World를 대입합니다. a에 Hello World가 쓰여집니다.
파이썬에는 이와 같이 변수에 어떤 값을 계속 담아도 아주 쉽게 값이 담깁니다.
문자를 담던, 숫자를 담던 그때 그때 알아서 값이 변수에 잘 담깁니다.
왜 변수에 문자를 담는것, 숫자를 담는것 등을 신경써야 하나요?
기본적으로 프로그래밍 언어들은 변수에 타입을 지정하는 경우와, 타입을 지정하지 않는 경우가 있습니다.
여기서 타입은 그 변수가 가질 수 있는 데이터의 형태를 의미합니다.
예를 들어서 Java와 같은 언어에서 a라는 변수에 "숫자" 라는 타입을 주면 그 a라는 변수는 무조건 숫자만 저장해야 합니다. "Hello World" 같은 문자는 저장할 수 없습니다
그러나 파이썬은 변수에 타입을 지정하지 않습니다. 그냥 간편하게 아무 값이나 대입하면 그때 그때 타입이 알아서 결정됩니다. a = 10 을 넣으면 a는 숫자가 되고 나중에 a = "Hello World" 를 다시 대입하면 a는 자동으로 문자열이 됩니다. 이것을 있어보이는 말로 동적 프로그래밍 언어(Dynamic Programming Language) 라고 표현합니다. 이 내용은 자료형(Data Type) 을 배우면 제대로 이해가 될겁니다. 일단은 간단히 하고 넘어갑니다.
마지막으로 a의 값을 출력하라고 지시합니다. a는 현재 마지막으로 대입된 Hello World가 들어있으므로 Hello World가 출력됩니다.
여러 변수 사용하기 + print 함수 심화
a = 10
b = 3.14
c = "Hello World"
print(a)
print(b)
print(c)
>>>
10
3.14
Hello World
앞에선 a라는 변수에 계속 대입하는 형태로 변수 1개만을 사용해 출력을 했는데 실제로 데이터가 여러개면 변수도 여러개를 만들어 관리해주는게 좋습니다.
위는 변수 a, b, c를 만들어 데이터를 저장하고 출력하는 예제입니다.
print(a)
print(b)
print(c)
하지만 변수 3개 출력을 위해 이렇게 print를 3번이나 써줘야 하는건 상당히 귀찮습니다.
사실 파이썬에선 변수 여러개를 출력하고 싶으면 한 줄이면 끝납니다.
a = 10
b = 3.14
c = "Hello World"
print(a, b, c)
>>>
10 3.14 Hello World
이렇게 콤마를 찍어서 출력해주면 여러개의 변수를 자동으로 공백을 1개씩 두고 출력해줍니다.
간단하죠? 어떤 값이던 print() 에서 콤마를 찍어서 여러개 주면 자동 공백을 통해 출력 가능합니다.
a = 10
b = 3.14
c = "Hello World"
print(a, b, c, sep = "")
>>> 103.14Hello World
만약에 콤마를 찍어서 여러개를 출력 시 자동 공백 대신 다른 문자를 사용하면 print 끝에 sep 옵션을 주시면 됩니다.
print 함수 맨 마지막에 sep = "공백 대신 원하는 구분 문자열" 로 지시를 해주시면 공백 대신에 저기 sep에 대입된 문자열이 나타나게 됩니다.
여기서 sep 는 Separator 의 약자로 한국어로 하면 "구분자" 라는 뜻 입니다. a, b, c 여러개를 출력할 때 쉽게 구분하기 위해서 보여주는 기본 문자가 공백인 것입니다. 따라서 sep의 기본값은 공백입니다.
print(a, b, c, sep = "")
이 코드의 의미는 내가 a, b, c 이 변수 3개를 출력하고 싶은데 중간의 구분자 (Separator) 를 공백 대신에 아무것도 없는 문자로 딱 붙여서 출력해줘~ 가 됩니다. 여러 변수를 공백 없이 출력해주라는 뜻이죠.
a = 10
b = 3.14
c = "Hello World"
print(a, b, c, sep = "\n")
>>>
10
3.14
Hello World
만약에 공백 대신에 여러개 변수를 출력할 때 엔터를 쳐달라고 명령하고 싶으면 다음과 같이 입력하면 됩니다.
a = 10
b = 3.14
c = "Hello World"
print(a)
print(b)
print(c)
이렇게 하면 아까 했던 위와 같은 방식과 동일한 코드가 되나 훨씬 더 효율적인 코드가 되게 됩니다.
a = 10
b = 3.14
c = "Hello World"
또한 위 처럼 여러개의 변수에 값을 각각 대입하는 경우 이 대입문을 또 한 줄로 적을 수 있습니다.
바로 아래처럼요
a, b, c = 10, 3.14, "Hello World"
각 자리에 맞게 a에는 10, b에는 3.14, c에는 "Hello World" 가 들어가게 됩니다.
a, b, c = 10, 3.14, "Hello World"
print(a, b, c)
>>> 10 3.14 Hello World
그래서 여러 변수를 공백에 따라 출력시키려면 다음과 같이 2줄의 코드를 작성하면 됩니다.
매우 아름답고 간편하죠? ㅎㅎ
사실 파이썬이 아닌 다른 언어에서 이런 코딩들은 상상도 하기 어렵답니다. 비슷하게 작성은 되나 파이썬보다 훨씬 코드도 길고 지저분한 경우가 대부분입니다. 파이썬에선 매우 간결한 표현으로 코딩을 쉽게할 수 있답니다. 속도만 포기하구요
변수 스왑하기(Swap)
a = 100
b = 10
현재 a라는 변수(공간)에 100이 저장되어 있고, b라는 변수(공간)에 10이라는 값이 저장되어 있습니다.
이때 a와 b의 값을 서로 교환(exchange)하고 싶으면 어떻게 해야 할까요? a에는 b의 값 10이, b에는 a의 값 100이.. 이렇게 서로의 값이 바뀌게요.
변수의 값을 상대방의 것과 서로 교환하는 행위를 보통 "Swap" 이라고 표현합니다.
*Swap : 교환품, 바꾸다, 교환하다
a = 100
b = 10
tmp = a
a = b
b = tmp
Swap을 구현하기 위해선 일반적인 방법으로는 다음과 같이 해야합니다.
a = b
b = a
a와 b를 바꾸기 위해선 다음과 같은 두줄의 간단한 코드를 기대하셨을 수 있습니다.
하지만 a = b를 수행하는 순간 오른쪽에서 왼쪽, 즉 b의 값이 a의 값에 그대로 덮어써져 버립니다.
그러면 저 코드가 실행된 순간 기존 a의 값은 영영 잃어버리게 됩니다. a는 b의 값을 가지게 되었습니다.
이후 b의 값에 a를 대입해봤자 a의 값은 b의 값이 되었기 때문에 똑같은 값을 지닌 변수가 2개 생기는 꼴이 되어버리는 것 입니다.
tmp = a
a = b
b = tmp
그렇기에 값을 바꾸고 싶다면 다음과 같이 임시(temporary)로 변수를 하나 두어 기존 a의 값을 저장해두고, a의 값을 b에 덮어 씌운 뒤 마지막으로 tmp 에 저장된 a의 값을 b에 옮겨줘야 변수 값 교환 동작 (Swap) 을 성공적으로 수행할 수 있습니다.
*위에서 tmp 는 temporary 를 줄여서 사용하는 임시 변수 이름입니다.
관례적으로 임시 변수명을 보통 tmp라고 하곤 합니다. (자기 마음이므로 따르지 않을 사람들은 안해도됨)
그렇지만! 매번 값을 바꾸는 행위는 많이 일어날 수 있고 그때마다 이렇게 매번 임시 변수를 만들어서 값을 저장하고 바꾸는건 햇갈리기도 하고 실수도 많이 나오고 코드도 지저분합니다.
따라서 파이썬은 swap 동작을 쉽게 할 수 있도록 특별한 방식을 지원합니다.
a = 100
b = 10
a,b = b,a
파이썬에서 swap을 하고 싶으면 그냥 위 코드처럼 하면 끝납니다. 간단하죠???
a, b = b,a 처럼 적으면 a와 b의 값의 서로 swap 되어 들어갑니다.
a, b, c = 10, 3.14, "Hello World"
a,b = b,a 는 앞에서 배운 구문을 조금 똑똑하게 사용한 것 뿐입니다.
a, b, c = 10, 3.14, "Hello World" 를 하면 각 자리에 맞게 a 는 10, b는 3.14, c는 Hello World가 들어갔듯이
a,b = b,a를 하면 b의 값은 a로, a의 값은 b로 들어가게 됩니다.
연속 대입
a = 100
b = a
c = b
d = c
print(a, b, c, d)
>>> 100 100 100 100
만약에 a,b,c,d 라는 변수 4개를 만들고 여기에 모두 100을 넣기 위해 다음과 같이 연속적으로 대입을 하였습니다.
물론 [블럭*a, b, c, d = 100, 100, 100, 100*] 으로 적어도 되긴 하는데 100을 여러번 쓰기 싫어서 다음과 같이 작성했습니다.
d = c = b = a = 100
print(a, b, c, d)
위 코드는 당연히 작동하지만 좀 더 읽기 좋게 위 처럼 쓸 수도 있습니다.
코드가 매우 간단해졌죠?
해당 코드랑 위에서 본 코드는 동작이 동일합니다.
또한 모두 100을 대입하려고 하는게 목적이므로 순서도 그렇게 중요하진 않습니다.
a = b = c = d = 100 으로 써도 상관없습니다.
연속 대입 (심화, 궁금한 사람만)
d = c = b = a = 100
해당 코드가 어떻게 해석되는지 해석 원리를 설명해드리고자 합니다.
궁금하신 분들만 보세요. 일반적으론 위에서 이해한 대로 100을 a,b,c,d 에 모두 대입한다 정도로 이해하시는것에 그치셔도 됩니다.
대입 연산자는 아까도 말씀드렸다 싶이 연산을 오른쪽에서 왼쪽으로 해석합니다. 따라서 맨 오른쪽에 있는 a = 100을 먼저 해석합니다. a 에 100이 그대로 대입됩니다. 그리고 중요한 사실.
대입 연산자는 실제로 연산 결과가 있습니다. 1 + 1의 결과값은 뭔가요? 2 입니다. 비슷하게 대입 연산자 역시 엄연한 연산자이므로 결과값이 있습니다.
a = 100 에서 연산의 결과는 바로 대입한 값입니다. a라는 변수에 100을 대입헀죠? 여기서 100이 바로 a = 100의 연산 결과가 되게 됩니다. (대입 당한 이후의 자기 자신 a값)
따라서 a = 100이 수행된 후에 a = 100 부분에 결과값 100이 그대로 던져집니다.
print(1 + 1) 을 했으면 1 + 1 이 그대로 출력되는게 아니라 1 + 1에 대한 연산 결과값 2가 1 + 1 부분에 던져져서 실제로는 2가 출력된 것과 동일하다고 이해하시면 됩니다.
그래서 이후로 다시 b = 100과 같이 해석되고 b에 100이 대입되게 됩니다.
그리고 b = 100 부분에 b의 연산 결과값 100이 다시 그대로 던져져서 같은 일이 반복됩니다
최후엔 c = 100 의 연산에 의해 d = 100 이라는 연산이 마지막에 수행됩니다.
아마 이 부분은 이해가 어려울 겁니다. 대입 연산자도 결과값이 있었다니..?
여기 부분은 이해가 안가시면 넘어가셔도 좋습니다.
이렇게까지 아실 필요는 아직까진 없어요. 아마 이건 C++에서 연산자 오버로딩 이라는 부분을 배우시다 보면 더 깊게 아시게 될겁니다 ㅎㅎ
일단 변수는 여기서 끝입니다. 다음 강좌는 주석과 기타 잡기술, 세미콜론에 대해 알아보겠습니다.
감사합니다.
참고
'=' 이 대입 연산자는 말그대로 값을 대입한다는 뜻입니다.
이처럼 a라는 변수에 30이라는 값을 대입한다는 뜻입니다.
여기서 중요한 것은 수학에서의 등호 '='와는 의미가 다르다는 것입니다.
즉, '=' 오른쪽에 있는 값을 왼쪽에 있는 변수로 대입한다는 뜻이 됩니다. 절대 같다는 뜻이 아니므로 명심해야 합니다.
저 부분을 "같다"라고 생각해서 발생하는 프로그래밍에서 휴먼에러가 생각보다 상당히 많습니다. 주의가 필요합니다.
https://opentutorials.org/module/3921/23589
대입 연산자 ‘=’ 는 오른쪽 계산식의 결과나 값을 왼쪽의 변수에 대입 하라는 의미로 사용됩니다.
https://velog.io/@shin6403/%EC%97%B0%EC%82%B0%EC%9E%90%EC%9D%98-%EC%A2%85%EB%A5%98%EC%99%80-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84Part.3-%EB%8C%80%EC%9E%85-%EC%97%B0%EC%82%B0%EC%9E%90
예를들어 A=B 수식에서 =의 역할은 B값을 A에 대입하라 이뜻이다.
대입연산의 결과 값은 해당 변수에 저장된 값이다.
대입연산 결과를 이용하면 연속 대입(cascading assignment) 연산을 수행할 수 있다.
'프로그래밍 강좌 > Python [리뉴얼중]' 카테고리의 다른 글
[파이썬 강좌] #6 input() 으로 입력 받기, 주석(Comment), 세미콜론 (0) | 2023.09.23 |
---|---|
[파이썬 강좌] #4 파이썬의 실행 원리 알아보기 (0) | 2023.08.06 |
[파이썬 강좌] #3-2 파이썬 훑어보기 (0) | 2023.08.03 |
[파이썬 강좌] #3-1 파이썬 훑어보기 (0) | 2023.08.01 |
[파이썬 강좌] #2 파이썬 개발환경 구축 (0) | 2023.05.06 |