4. 변수, 값, 표현식#

프로그래밍 언어를 분류하는 여러 기준 중에 하나가 명령형 프로그래밍 언어인지 여부이다. 파이썬은 C, C++, C#, 자바, 자바스크립트, Rust 등처럼 명령형 프로그래밍 언어이다. 명령형 프로그래밍언어의 핵심 기능 중의 하나가 바로 컴퓨터 메모리에 저장된 값을 조작하는 일이다. 값을 저장하고 조작하기 위해 값을 가리키는 이름이 필요한데 이를 위해 변수를 사용한다.

컴퓨터 프로그램은 간단하게 말해 변수를 조작하여 원하는 값을 만들어내는 명령문의 모음집이라고 할 수 있다. 이번 장에서는 변수의 활용법과 함께 파이썬에서 기본으로 제공하는 값의 종류와 값을 표현하는 표현식에 대해 알아본다.

4.1. 변수와 값#

values은 컴퓨터에 저장해서 활용할 수 있는 대상이며, 정수와 부동소수점이 대표적인 값들이다. 반면에 변수variables는 컴퓨터 메모리에 저장된 값을 가리키는 이름이다.

참고로 무리수를 포함한 무한소수와 소수점 이하의 자릿수가 너무 큰 유한소수는 컴퓨터의 용향 한계로 인해 제대로 다뤄질 수 없다. 파이썬을 포함하여 모든 프로그래밍 언어는 유한소수와 무한소수 모두 소수점 이하 적당한 자리에서 끊어서 저장한다. 소수점 이하 자릿수를 얼마로 할 것인가는 프로그램이 활용되는 설정 환경에 따라, 또는 컴퓨터 사양에 따라 달라진다. 소수점 이하의 자릿수가 일정하지 않게 처리된다는 의미에서 유한소수의 자료형을 부동소수점floating points이라 부른다.

정수, 유한소수와 더불어 a, b, c, …, , , , …, !, :, ;, _, (, ), … 처럼 컴퓨터 키보드를 이용하여 입력할 수 있는 임의의 문자, 숫자, 기호로 구성된 문자열도 값으로 취급된다. 예를 들어, 파이썬 좋아요;-), Hello, World! 등과 같은 문자열도 값이다. 이외에 파이썬은 리스트, 튜플, 집합, 사전 등 다양한 종류의 값을 제공하며, 앞으로 하나씩 상세히 살펴볼 것이다.

4.1.1. 변수 할당#

변수 할당variable assignment 명령문은 하나의 변수에 하나의 값을 할당하며, 이 과정을 통해 변수가 선언된다. 할당된 값은 컴퓨터 메모리 어딘가에 저장되며 선언된 변수는 할당된 값이 저장된 메모리의 주소 정보를 알고 있게 된다. 쉽게 말해 변수는 자신에게 할당된 값이 어디에 저장되어 있는지 알고 있고 필요하면 언제든지 그 값을 대신할 수 있다.

변수 할당 명령문의 형식은 다음과 같다.

변수 = 

예를 들어, 아래 네 개의 할당 명령문은 서로 다른 종류의 값을 가리키는 네 개의 변수를 선언한다.

  • greetings 변수: '안녕하세요!' 라는 문자열을 할당받는다.

greetings = '안녕하세요!'
  • num 변수: 정수 17을 할당받는다.

num = 17
  • pi 변수: 유한소수 3.14를 할당받는다.

pi = 3.14
  • scores 변수: 세 개의 시험점수로 구성된 리스트를 할당받는다.

scores = [92, 87, 100]

선언된 변수와 할당된 값

선언된 변수는 할당된 값과 동등하게 취급된다. 예를 들어 '안녕하세요!' 문자열이 필요하면 greetings 변수를 대신 사용할 수 있다. 아래 코드에서처럼 greetings 변수명만 적은 다음 실행하면 변수에 할당된 값인 "안녕하세요"를 확인해준다.

greetings
'안녕하세요!'

변수 numpi 가 각각 정수와 부동소수점을 가리키기에 연산에도 사용할 수 있다.

num + 2
19
2 * pi
6.28

변수 scores가 시험점수로 구성된 리스트를 가리키기에 아래 코드에서처럼 시험점수의 평균값을 리스트 인덱싱을 이용하여 계산할 수 있다. 참고로 정수의 나누셈 결과는 항상 부동소수점으로 처리됨에 주의한다.

mean = (scores[0] + scores[1] + scores[2])/3
mean
93.0

변수 재할당

변수 재할당은 변수에 할당된 값을 다른 값으로 대체하는 일이다. 설명을 위해 변수 x에 정수 5를 할당하자.

x = 5

변수 x는 정수 5를 가리킨다.

x
5

이제 변수 할당 명령문을 아래처럼 다시 실행해본다.

x = 7

이제 변수 x는 정수 5가 아닌 정수 7을 가리킨다.

x
7

변수 업데이트

변수 업데이트variable update는 이미 선언된 변수에 대해 변수 재할당을 실행할 때 변수에 이미 할당되어 있던 값을 이용하는 방식이다. 예를 들어 아래 코드는 변수 x가 기존에 가리키던 값 7에 1을 더한 값 8을 다시 변수 x에 재할당한다.

x = x + 1
print(x)
8

반면에 아래 명령문은 변수 x가 가리키던 값 8에서 1을 뺀 값인 7을 다시 변수 x에 할당한다.

x = x - 1
print(x)
7

변수 업데이트 명령문을 보다 간편하게 표현할 수 있다. 예를 들어 아래 명령문은 x = x+1을 대신한다.

x += 1
print(x)
8

반면에 아래 명령문은 x = x / 2를 대신한다.

x /= 2
print(x)
4.0

프로그래밍에서 많이 사용되는 간편한 변수 업데이트 형식은 다음과 같다.

간편 변수 업데이트

실제 의미

x += k

x = x + k

x -= k

x = x - k

x *= k

x = x * k

x /= k

x = x / k

x **= k

x = x ** k

x //= k

x = x // k

x %= k

x = x % k

간편 변수 업데이트 명령문은 while 반복문에서 많이 활용된다. 예를 들어 3.7.3절에서 while 반복문을 이용하여 1부터 10까지의 정수들의 합을 계산한 코드는 다음과 같았다.

one2ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

index = 0
sum = 0                         # 항목을 차례대로 더한 값. 시작은 0부터.

while index < 10:
    sum = sum + one2ten[index]  # 변수 업데이트
    index = index + 1           # 변수 업데이트
    
print("1부터 10까지 정수의 합:", sum)
1부터 10까지 정수의 합: 55

위 코드에서 while 반복문의 본문에 포함된 두 개의 변수 업데이트 명령문 모두 간편 변수 업데이트 명령문으로 대체할 수 있다.

one2ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

index = 0
sum = 0                         # 항목을 차례대로 더한 값. 시작은 0부터.

while index < 10:
    sum += one2ten[index]       # 간편 변수 업데이트
    index += 1                  # 간편 변수 업데이트
    
print("1부터 10까지 정수의 합:", sum)
1부터 10까지 정수의 합: 55

4.1.2. 변수명 작성법#

변수명, 즉 변수의 이름은 할당되는 값과 연관된 이름을 사용하는 게 좋다. 예를 들어, 농구 시합에서 A 팀의 점수 72와 B 팀의 점수 80을 가리키는 두 개의 변수를 아래와 같이 xy로 하면 코드만 보고서는 변수의 의미를 파악하기 어렵다.

x = 72
y = 80

반면에 아래처럼 A_score, B_score 등으로 변수를 정하면 코드에 사용된 변수의 의미를 보다 잘 이해할 수 있다.

A_score = 72
B_score = 80

변수명 작성 규칙

변수명을 지정할 때 지켜야 하는 몇 가지 규칙이 있다.

  • 알파벳, 숫자, 밑줄 기호 _ 등을 임의로 조합한다.

  • 다음 기호들은 사용할 수 없다.

    ., !, +, -, *, /, %, @, ~
    
  • 공백space을 포함할 수 없다.

  • 숫자로 시작할 수 없다.

  • 파이썬에서 특별한 역할을 수행하는 아래 키워드keyword를 그대로 변수로 사용할 수 없다. 파이썬 프로그래밍 언어의 키워드는 파이썬 최신 버전 기준으로 총 35개이다. 각 키워드의 역할은 필요할 때마다 설명될 것이다.

    False      await      else       import     pass
    None       break      except     in         raise
    True       class      finally    is         return
    and        continue   for        lambda     try
    as         def        from       nonlocal   while
    assert     del        global     not        with
    async      elif       if         or         yield
    

변수명으로 허용되지 않는 사례 몇 가지를 살펴보자.

  • 숫자로 시작: 숫자로 시작하면 안된다는 규칙을 어겼기에 구문 오류(SyntaxError)가 발생한다.

3dogs = '강아지 세 마리'
  Cell In[27], line 1
    3dogs = '강아지 세 마리'
    ^
SyntaxError: invalid decimal literal
  • 마침표 활용: first.second를 변수로 지정하면 마침표로 인해 firstsecond가 구분된다. 그런데 first의 의미가 미리 지정되지 않았다는 의미의 이름 오류(NameError)가 발생한다.

first.second = "1, 2"
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[28], line 1
----> 1 first.second = "1, 2"

NameError: name 'first' is not defined
  • 공백 사용: 변수명에 공백이 허용되지 않기에 구문 오류(SyntaxError)가 발생한다.

big number = 100000000
  Cell In[29], line 1
    big number = 100000000
        ^
SyntaxError: invalid syntax
  • 파이썬 키워드 사용: 예를 들어, False는 거짓을 나타내는 부울값(진릿값)이기에 다른 의미로 사용되어서는 안된다. 따라서 구문 오류(SyntaxError)가 발생한다.

False = 0
  Cell In[30], line 1
    False = 0
    ^
SyntaxError: cannot assign to False

소문자와 대문자 구분하기

프로그래밍에서 영어 알파벳의 소문자와 대문자는 엄연히 구분된다. 예를 들어, 'hello''Hello'는 서로 다른 문자열이다.

'hello' == 'Hello'
False

4.2. 연산자와 함수#

사칙연산은 정수, 유리수, 실수 등을 대상으로 하는 수학 계산을 의미한다. 반면에 프로그래밍에서는 정수, 부동소수점 이외에 문자열, 리스트 등 보다 다양한 종류의 값을 다루기에 이들에 대한 연산도 함께 제공한다.

여기서는 먼저 사칙연산의 정의가 특정한 방식으로 문자열과 리스트에 대해서도 확장되어 사용됨을 보여준다. 앞으로 보다 다양한 종류의 연산을 살펴본다.

연산자

연산자operator는 연산을 위해 사용되는 기호를 가리킨다. 프로그래밍에서 가장 많이 사용되는 연산자는 다음과 같다.

  • 사칙 연산자: +, -, *, /

  • 거듭제곱 연산자: **

  • 비교 연산자: <, >, <=, >=

위 연산자들이 정수와 부동소수점의 연산에 사용되면 수학에서의 연산과 동일하다. 그런데 +, * 등 일부 연산자는 문자열, 리스트 등의 연산에 특별한 방식으로 활용된다. 설명을 위해 아래 두 문자열을 이용한다.

first = '파이썬, '
second = '안녕!'
  • 문자열과 + 연산자: 두 개의 문자열과 함께 + 연산자를 적용하면 주어진 두 개의 문자열을 그대로 이어붙인 문자열을 계산한다.

greetings = first + second
greetings
'파이썬, 안녕!'
  • 문자열과 * 연산자: 문자열과 정수를 곱하면 주어진 문자열을 지정된 정수만큼 복제해서 이어붙인 문자열을 계산한다.

greetings * 3
'파이썬, 안녕!파이썬, 안녕!파이썬, 안녕!'

리스트에 대해서도 + 연산자는 이어붙이기를, * 연산자는 복제 후 이어붙이기를 실행한다.

first_languages = ['파이썬', 'C', '자바']
second_languages = ['C++', 'C#', '자바스크립트', 'Node.js']
  • 리스트와 + 연산자: 두 개의 리스트를 이어붙여 하나의 리스트를 계산한다.

languages = first_languages + second_languages
languages
['파이썬', 'C', '자바', 'C++', 'C#', '자바스크립트', 'Node.js']
  • 리스트와 * 연산자: 주어진 리스트를 지정된 정수만큼 복제해서 이어붙인 리스트를 계산한다.

languages * 2
['파이썬',
 'C',
 '자바',
 'C++',
 'C#',
 '자바스크립트',
 'Node.js',
 '파이썬',
 'C',
 '자바',
 'C++',
 'C#',
 '자바스크립트',
 'Node.js']

연산자 우선순위

여러 종류의 연산자를 이용할 경우 연산자들 사이의 우선순위를 고려해야 한다. 그렇지 않으면 의도치 않은 값이 계산될 수 있다. 연산자 우선순위는 수학에서 배운 것과 동일하다.

  • 괄호 안에 있는 표현식을 가장 먼저 계산한다.

    2 * (3 - 1) = 2 * 2 = 4
    (1 + 1) ** (5 - 2) = 2 ** (5 - 2) = 2 ** 3 = 8
    
  • 거듭제곱이 사칙연산보다 우선순위가 높다.

    3 ** 2 * 2 = (3 ** 2) * 2 = 9 * 2 = 18
    3 * 2 ** 2 = 3 * (2 ** 2) = 3 * 4 = 12
    
  • 곱셈과 나눗셈이 덧셈과 뺄셈보다 우선순위가 높다.

    2 * 3 - 1 = (2 * 3) - 1 = 6 - 1 = 5
    6 + 4 / 2 = 6 + (4 / 2) = 6 + 2.0 = 8.0
    
  • 곱셈과 나눗셈은 서로 우선순위가 같다.

  • 덧셈과 뺄쎔도 서로 우선순위가 같다.

  • 같은 우선순위를 갖는 사칙 연산자가 연속해서 사용되면 왼쪽에 위치한 연산부터 먼저 실행된다.

    60 / 2 * 3 = (60 / 2) * 3 = 30.0 * 3 = 90.0
    

    만약에 오른쪽에 위치한 곱셈을 먼저 실행하면 다른 결과가 나온다.

    60 / (2 * 3) = 60 / 6 = 10.0
    
  • 거듭제곱이 연속해서 나오면 오른쪽 거듭제곱부터 실행된다.

    2 ** 3 ** 2 = 2 ** (3 ** 2) = 2 ** 9 = 512
    

    만약에 왼쪽부터 거듭제곱을 계산하면 다른 결과가 나온다.

    (2 ** 3) ** 2 = 8 ** 2 = 64
    

함수

파이썬과 같은 프로그래밍 언어는 연산자에 의해 계산된 값을 저장한 다음에 필요에 따라 활용하는 기능을 제공한다. 예를 들어, 아래 코드는 1에 2를 더한 값을 계산하여 변수 onePtwo에 저장한 다음에 그 값이 짝수이면 1을, 홀수면 0을 출력한다.

onePtwo = 1 + 2

if onePtwo % 2:
    print(1)
else:
    print(0)
1

계산된 값을 저장하고 활용하는 일은 일반적인 함수에 대해서도 동일하게 적용된다. 예를 들어 아래 코드는 1과 2의 최댓값/최솟값을 계산하여 활용한다.

max_1_2 = max(1, 2)
print("1과 2의 최댓값:", max_1_2)
1과 2의 최댓값: 2
min_1_2 = min(1, 2)
print("1과 2의 최솟값:", min_1_2)
1과 2의 최솟값: 1

함수에 대해서는 5장에서 자세히 설명한다. 여기서는 함수가 계산한 값을 저장하고 활용할 수 있다는 것만 기억한다.

4.3. 표현식#

17, 3.14, '안녕하세요!' 등은 변수에 할당하거나 연산자 또는 함수의 인자로 사용할 수 있는 값이다. 그런데 앞서 보았듯이 변수 또한 동일한 방식으로 사용될 수 있다. 예를 들어 아래 코드에서 선언된 변수 num은 정수 17일 가리키면서 동시에 정수 17과 동일한 방식으로 활용될 수 있다.

num = 17
  • 연산에서 활용

num + 2 # 17 더하기 2
19
  • 함수의 인자에서 활용

max_17 = max(num+2, num//2) # 19와 8의 최댓값
print(max_17)
19
min_17 = min(num*2, num/2) # 38과 8.5의 최솟값
print(min_17)
8.5

num + 2, max(num+2, num//2), min(num*2, num/2) 등처럼 변수와 값을 연산자 또는 함수와 결합한 식을 실행하면 새로운 값이 계산된다. 즉, 2, num, num + 2, max(num+2, num//2), min(num*2, num/2) 모두 특정 값을 표현하며, 이런 의미에서 표현식expression이라 불린다.

파이썬을 포함하여 대다수의 프로그래밍 언어에서는 정수와 부동소수점을 이용한 계산뿐만 아니라 다양한 종류의 연산자와 함수를 활용하여 특정 값을 표현하는 표현식을 작성할 수 있다. 예를 들어 3장에서 간단하게 소개한 문자열, 리스트 등도 값이며, 아래 표현식들처럼 문자열 연산과 리스트 인덱싱 등도 특정 값을 표현하는 표현식이다.

"파이썬," + " 안녕!"
['파이썬', 'C', "자바", "Rust"][0]

아래 두 코드가 위 표현식을 이용한 계산 결과를 보여준다. print() 함수는 임의의 개수의 표현식을 인자로 받아 각각의 표현식이 표현하는 값을 화면에 출력한다.

hello = "파이썬," + " 안녕!"
print(hello)
파이썬, 안녕!
python = ['파이썬', 'C', "자바", "Rust"][0]
print("가장 많이 활용되는 프로그래밍 언어:", python)
가장 많이 활용되는 프로그래밍 언어: 파이썬

표현식의 자료형

표현식은 표현하는 값의 자료형을 갖는다.

type(num + 2)
int
type(max(num+2, num//2))
int
type(min(num*2, num/2))
float
type("파이썬," + " 안녕!")
str
type(['파이썬', 'C', "자바", "Rust"][0])
str

4.4. 입력과 출력#

아래 코드를 실행한 다음에 키보드에서 숫자 13을 입력하고 Enter 키를 누르면 최종적으로 '맞았습니다!'가 출력된다.

print("숫자맞히기 게임에 환영합니다.")

secret = 13
guess_str = input("10부터 19 사이의 숫자 하나를 입력하세요: ") 
guess = int(guess_str)

if secret == guess:
    print("맞았습니다!")
else:
    print("틀렸습니다!")
숫자맞히기 게임에 환영합니다.
맞았습니다!

이유는 secret 변수에 할당되어 있는 값이 13이고 guess 변수에는 사용자가 입력한 값인 13이 할당되는 데 그러면 if ... else ... 조건문에 사용된 논리식 guess == secretTrue로 계산되어 print("맞았습니다!")가 실행되기 때문이다.

출력: print() 함수

지정된 값을 화면에 출력하려면 print() 함수를 활용한다.

if secret == guess:
    print("맞았습니다!")
else:
    print("틀렸습니다!")
맞았습니다!

입력: input() 함수

사용자가 입력한 13은 input() 함수에 의해 변수 guess_str에 할당된다. input() 함수의 사용법은 다음과 같다.

input("입력값 안내 문자열")

input() 함수의 인자는 사용자로부터 입력되어야 하는 값에 대한 정보를 알려주는 안내문의 역할을 수행할 뿐이며 사용자의 입력값을 처리하는 방식에는 전혀 영향을 미치지 않는다. 따라서 안내 문자열을 생략할 수 있다.

입력값 안내 문자열을 지정하고 실행하면 안내 문자열이 먼저 출력되고 사용자가 Enter 키를 입력할 때까지 기다린다. input() 함수가 사용자로부터 받아들이는 값은 Enter 키가 입력되기 전까지 입력된 문자와 기호로 이루어진 문자열이다. 아래 코드를 실행할 때 사용자가 1과 3을 연속으로 입력한 후에 Enter 키를 치면 input() 함수는 문자열 '13'을 입력값으로 처리한다.

input("10부터 19 사이의 숫자 하나를 입력하세요: ")
10부터 19 사이의 숫자 하나를 입력하세요:  13
'13'

입력값 안내 문자열을 사용하지 않으면 아무런 정보 없이 사용자가 Enter 키를 입력할 때까지 기다린다. 사용자 입력값은 정수, 부동소수점뿐만 아니라 임의의 키의 조합이 될 수 있다. 아래 코드를 실행한 후에 파이썬을 입력한 후에 Enter 키를 치면 문자열 '파이썬'input() 함수의 입력값으로 처리된다.

input()
 파이썬
'파이썬'

input() 함수의 입력값은 무조건 문자열로 지정된다. 이런 이유로 guess_str 변수에 할당된 값은 정수 13이 아니라 문자열 '13'이다.

guess_str
'13'
type(guess_str)
str

그런데 사용자가 입력해서 맞혀야 하는 값은 정수 13이고 파이썬은 정수와 문자열을 비교하면 항상 False로 계산한다. 이는 정수 13과 문자열 '13'에 대해서도 동일하다.

13 == '13'
False

따라서 int() 함수를 이용하여 변환된 값을 guess 변수에 할당한 후에 secret에 할당된 정수 13과 비교하는 if ... else ... 조건문을 실행한다. 만약에 이 과정을 거치지 않고 secret == guess_str을 사용하면 무조건 "틀렸습니다!"가 출력될 것이다. 이유는 앞서 설명한 대로 secret == guess_strFalse로 계산되기 때문이다.

if secret == guess_str:
    print("맞았습니다!")
else:
    print("틀렸습니다!")
틀렸습니다!

따라서 guess_str에 할당된 값을 정수형으로 변환한 다음에 secret 변수가 가리키는 값과 동일한지 여부를 판단해야 한다.

guess == int(guess_str)
True

또한 if .. else ... 조건문도 guess를 대신 이용해야 13이 제대로 입력되었음을 확인받을 수 있다.

if secret == guess:
    print("맞았습니다!")
else:
    print("틀렸습니다!")
맞았습니다!

int(input()) 활용

앞서 입력된 값을 처리하기 위해 두 개의 변수를 사용하였다.

guess_str = input("10부터 19 사이의 숫자 하나를 입력하세요: ") 
guess = int(guess_str)

그런데 어차리 정수로 변환된 값을 사용할 것이기에 guess_str 변수를 사용하지 않으면서 직접 guess를 다음과 같이 정의해도 된다.

guess = int(input("10부터 19 사이의 숫자 하나를 입력하세요: "))

즉, guess_str 변수가 가리키고 있는 input() 함수 호출 표현식 그대로를 int() 함수의 인자로 지정한다.

이제 secret 변수에 저장된 13을 맞히는 코드를 다음과 같이 작성할 수 있다.

print("숫자맞히기 게임에 환영합니다.")

secret = 13
guess = int(input("10부터 19 사이의 숫자 하나를 입력하세요: "))

if secret == guess:
    print("맞았습니다!")
else:
    print("틀렸습니다!")
숫자맞히기 게임에 환영합니다.
10부터 19 사이의 숫자 하나를 입력하세요:  13
맞았습니다!

4.5. 필수 예제#

참고: (필수 예제) 변수, 값, 표현식

4.6. 연습문제#

참고: (연습) 변수, 값, 표현식