4  파이썬의 기본적인 값과 계산

4.1 리터럴(Literal)

프로그래밍 언어에서 리터럴(literal)이라고 하는 것은 다른 과정을 거치지 않고 직접 표현된 값을 의미한다. 42, "hello", [1, 2, 3] 등이 다 리터럴이다. 파이썬에서는 리터럴 방식으로 값을 표현하는 방식은 다음과 같다.

  • 정수: 숫자를 그대로 쓴다.
42 # 정수
42
  • 부동 소수점 수: 소수점을 사용하여 작성한다. 어떤 경우는 eE을 사용한다.
3.14 # 부동 소수점 수
3.14
  • 문자열: 따옴표로 묶어서 작성한다. 작은따옴표 또는 큰따옴표로 감싼다.
"hello" # 문자열
'hello'
  • 불리언: True 또는 False를 사용한다.
True # 불리언
True
  • 리스트: 대괄호로 묶어서 작성한다. 요소들은 쉼표로 구분한다.
[1, 2, 3] # 리스트(list)
[1, 2, 3]
  • 튜플: 괄호로 묶어서 작성한다. 요소들은 쉼표로 구분한다.
(1, 2, 3) # 튜플(tuple)
(1, 2, 3)
  • 딕셔너리: 중괄호로 묶어서 작성한다. 키와 값은 콜론으로 구분한다.
{"a": 2, "b": 3} # 딕셔너리(dictionary)
{'a': 2, 'b': 3}
  • 집합: 중괄호로 묶어서 작성한다. 요소들은 쉼표로 구분한다.
{1, 2, 3, 1, 3, 3} # 집합(set)
{1, 2, 3}

4.2 표현식(expression), 변수(variable), 할당문(assignment statement)

  • 프로그래밍 언어에서 하나의 값으로 귀결시킬 수 있는 코드를 표현식(expression)이라고 한다. 표현식은 변수, 리터럴, 연산자, 함수 호출 등으로 구성되는데 아래에서 42, 42 + 2, abs(-3.14), 42 + 2 + abs(-3.14) 등이 모두 표현식의 일종이다. 이 부분들이 모두 하나의 값으로 귀결되기 때문이다.
42 + 2 + abs(-3.14)
47.14
  • 표현식을 평가하여 그 결과를 변수에 저장하는 것을 할당(assignment)이라고 한다. 할당문은 표현식과 할당 연산자 =로 구성된다. 아래 코드에서 a = 42는 표현식 42를 평가하여 그 결과를 변수 a에 저장하는 할당문이다. 할당문은 표현식이 아니라 문(statement)이다. 문은 하나의 온전한 명령이다.
a = 42
  • 파이썬에서 변수의 이름(name)은 알파벳 대문자, 소문자, 숫자, 언더스코어(_)로 만들 수 있는데, 숫자로는 시작하면 안 된다. 또 파이썬에서는 대소문자를 구분한다(case-sensitive). 즉, aA는 다른 변수이다.
a = 42
A = 42
  • 파이썬은 “dynamically typed” 언어라고 하는데, 이것은 변수의 타입(type)을 미리 선언하지 않고 변수의 값의 타입에 따라 타입이 결정된다는 뜻이다. 변수의 타입이란 값의 종류를 말한다. 정수, 부동 소수점 수, 리스트, 튜플, 셋 등이 모두 데이터 타입(data type)이다. 같은 변수에 재할당을 하면 원래의 타입을 잃고 새로운 타입의 값으로 대체된다.
a = 42
a = "hello"
a
'hello'

4.3 변수(variable)와 객체(object)

파이썬은 객체 지향 언어(object-oriented programming, OOP)이다. 객체 지향 언어의 특징은 객체(object)를 기반으로 하는 것이다. 객체는 데이터와 그 데이터를 처리하는 메서드(method)로 구성된다. 객체 지향 언어 변수(variable)은 객체를 가리키는 이름으로, 굳이 서로 구분할 필요가 없다.

4.4 객체의 종류: mutable vs immutable

Python 언어의 객체는 소유한 값을 바꿀 수 있는지 여부에 따라 mutable과 immutable로 나뉜다.

  • Immutable: 자신이 가진 값을 바꿀 수 없는 객체로 숫자, 문자열, 튜플 등이 여기에 속한다.
  • Mutable: 자신이 가진 값을 바꿀 수 있는 객체로 리스트, 딕셔너리, 셋 등이 여기에 속한다.

이것은 변수의 이름에 대한 것이 아니고, 메모리에 존재하는 것을 기준으로 한다. 어떤 객체가 가진 값을 대상으로 메모리의 위치는 바꾸지 않으면서 값을 바꿀 수 있을 때 mutable이라고 한다. Immutable 객체는 메모리의 위치를 바꾸지 않고서는 값을 바꾸지 못한다.

파이썬에 내장된 id() 함수는 메모리 위치를 표현하는 정수를 반환하는 함수이다. 이 함수를 사용하면 mutable과 immutable 객체의 차이를 확인할 수 있다.

Mutable 객체의 대표적인 예는 파이썬 list이다. 다음 코드를 보면 리스트 한 요소의 값을 바꾸어도 id() 값은 바뀌지 않음을 볼 수 있다. 이 경우에는 메모리의 위치는 바뀌지 않고 값만 바뀌었기 때문이다.

my_list = [1, 3, 5, 7, 9]
print(id(my_list))
# 0번 인덱스의 값을 바꿈 
my_list[0] = 100
print(id(my_list))
4579533184
4579533184

Immutable 객체의 대표적인 예는 문자열(str)이다. Immutable 객체의 값을 바꾸려 하면 오류가 발생한다.

my_str = 'hnee'
my_str[0] = 'k'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[14], line 2
      1 my_str = 'hnee'
----> 2 my_str[0] = 'k'

TypeError: 'str' object does not support item assignment

Immutable 객체의 값을 바꾸려면, 정확히 말하면 주어진 이름으로 바뀐 값을 가지게 하려면, 새로운 객체를 만들어야 한다(새로운 객체여서 기존 객체의 id()와 다르다). 같은 이름 my_str이 새로운 값 knee를 가지게 되었지만, 바뀐 my_str은 원래의 my_str이 아니다. 이를 id() 함수로 확인할 수 있다.

my_str = 'hnee'
print(id(my_str))
my_str = 'knee'
print(id(my_str))
5215789648
5221741456

4.5 두 객체의 비교(comparison)

파이썬 비교 연산자에는 다음과 같은 것들이 있고, 비교의 결과는 불리언(True, False)이다.

  • >, >=, <, <=: 변수의 값을 비교한다.
  • ==, !=: 변수의 이 같은지, 다른지 비교한다.
  • is, is not: 변수의 값은 물론이고 id()까지 같은지를 확인한다.

비교의 대상에 따라 파이썬이 어떤 방식으로 비교하는지 이해할 필요가 있다.

  • 정수 또는 부동 소수점 수인 경우에는 수학에서 배우는 것과 같이 비교 연산자가 작동한다.
1 < 2, 1 > 2, 1 <= 2, 1 >= 2, 1 == 2, 1 != 2
(True, False, True, False, False, True)
  • 문자열(str)은 알파벳 순서, 좀 더 일반화하면 유니코드 순서에 따라 비교된다. 쉽게 생각하면 사전에 나오는 순서로 비교된다. 뒤에 나오는 단어가 더 크다.
'apple' < 'banana', 'apple' > 'banana', 'apple' <= 'banana'
(True, False, True)
'apple' == 'banana', 'apple' != 'banana', 'apple' == 'apple'
(False, True, True)
  • 시퀀스(sequence) 객체(list, tuple 등)은 요소의 순서대로 비교해 나간다. 맨 처음은 처음 요소끼리 비교하고, 거기서 결정되지 않으면 그 다음으로 넘어간다. 다음 리스트 비교에서는 처음 두 요소의 값이 같아서 세 번째 요소에 이르러서야 비교가 결정된다.
[1, 2, 3] < [1, 2, 4]
True

4.5.1 파이썬에 같음을 확인하는 방법

파이썬에는 “같음”을 확인할 수 있는 연산자가 2개 있다.

  • ==, !=: 변수의 이 같은지 확인한다.
  • is, is not: 변수의 값은 물론이고 id()까지 같은지를 확인한다.

다음은 두 개의 리스트를 비교한 것이다. ==를 통해서 값이 같은지, is를 통해서 id() 값이 같은지를 확인할 수 있다.

a = [1, 2, 3]
b = [1, 2, 3]
a == b, a is b
(True, False)

필요에 따라 적절히 사용해야 하는데, 대부분의 경우는 값을 비교하는 ==을 사용할 것이다. is가 사용되는 대표적인 경우는 None인지 확인하는 경우이다.

if x is None:
    ...

4.6 파이썬에서 블록(block)과 들여쓰기(indentation)

파이썬에서 블록(block)은 들여쓰기(indentation)로 구분한다. 들여쓰기는 공백 문자(space)로 구성되며, 보통 4개의 스페이스를 사용하는 것이 관례이다. 같은 블록 안에서 스페이스는 일정하게 사용해야 한다. 어떤 곳에서 2개의 스페이스를 쓰고, 어떤 곳에서는 4개의 스페이스를 쓰면 안 된다.

다음은 if 문과 if-else 문의 예이다.

x = 42
if x > 0:
    print("x is positive")
x is positive
if x > 0:
    print("x is positive")
else:
    print("x is not positive")
x is positive

4.7 사칙 연산 등 수학 연산

파이썬에서 사칙 연산 등 수학 연산을 하는 방법은 다음과 같다.

  • +: 덧셈
  • -: 뺄셈
  • *: 곱셈
  • /: 나눗셈
  • %: 나머지
  • **: 거듭제곱
1 + 2, 1 - 2, 1 * 2, 1 / 2, 15 % 2, 3 ** 2
(3, -1, 2, 0.5, 1, 9)
  • //: 몫
15 // 2
7

//은 몫을 반환하는 데, 영어로 floor division이라고 한다. 그래서 숫자로 이어진 눈금자가 있을 때 소수점은 버리고 읽는다. 그래서 3.53으로 읽고, -3.5-4가 된다.

7 // 2
3
-7 // 2
-4

나머지 연사자 %는 오른쪽 연사자의 부호를 따라간다.

7 % 2
1
-7 % -2
-1
  • 내장 함수 abs()는 절대값을 반환한다.
abs(-3.14)
3.14
  • 내장 함수 round()는 소수점 자리를 조정한다.
round(3.14)
3
round(3.14, 1)
3.1

참고로 시퀀스에서 +를 적용하면 Concatenation(합쳐짐), *을 적용하면 반복된다. 섹션 5.3.7을 참고한다.

4.8 불리언 값과 불리언 맥락

불리언 값은 True 또는 False이다. 불리언 값은 조건문(conditional statement)에서 사용된다. 제일 흔한 조건문인 if 문은 다음과 같은 구조를 가지고 있다.

if <조건>:
    문장들...

그래서 다음과 같이 사용한다.

x = 42
if x > 0:
    print("x is positive")
x is positive

다음과 같은 if 문도 가능하다.

x = 42
if x:
    print("x is positive")
x is positive

결과가 출력되는 것은 xTrue로 평가되었기 때문이다. 그리고 실제 코드에서는 이와 같이 비교 연산자/논리 연산자를 사용하지 않고 직접 객체를 불리언 맥락(boolean context)에 넣어서 조건을 표현하는 경우가 많다. 불리언 맥락에 넣는다는 것은 객체를 조건문에 넣어서 사용한다는 의미이다.

x = 42
if x:
    print("x is positive")

그러면 파이썬에서 객체를 불리언 맥락에 넣을 때, 어떤 객체가 True로 평가되고, 어떤 객체가 False로 평가되는지 알아야 한다.

4.8.1 불리언 맥락

파이썬의 bool(Boolean) 타입은 True 또는 False 값을 가진다. 그리고 이런 값을 대상으로 연산을 할 때 and, or, not이라는 연산자를 사용한다. 그런데 실제 파이썬 코드에서 True 또는 False 값을 직접 사용하는 일은 거의 없다. 불리언 맥락에 따라 논리가 적용된다. 불리언 맥(Boolean context)란 다음과 같은 상황을 말한다.

  • if, while 문 안에서의 표현식
  • and, or, not 연산자의 피연산자

파이썬은 다음 값들을 불리언 맥락에서 False 값으로 인식한다. 이런 경우를 제외하고는 모든 True로 인식한다.

  • False
  • None
  • 정수나 부동 소수점 수 등 숫자의 값이 0인 경우
  • 아무런 요소를 가지고 있지 않은 컨테이너: 빈 문자열(""), 빈 리스트([]), 빈 딕셔너리({}), 빈 셋(set())

어떤 변수 x가 파이썬 인터프리터가 True로 보는지, False로 보는지 확인할 때는 bool() 내장함수를 사용한다. 불리언 맥락에서 인터프리터내 암묵적으로 이 함수를 실행하여 그게 참인지 거짓인지를 확인하고 연산을 수행한다는 뜻이다.

from fractions import Fraction
from decimal import Decimal

i = False
h = None
j = 0.0
k = Fraction(0, 3)
l = Decimal('0.0')
m = set()
n = []
o = {}
p = ""

for v in [i, h, j, k, l, m, n, o, p]:
    print(bool(v))
False
False
False
False
False
False
False
False
False

4.8.2 내장 객체의 constructor들을 인자 없이 사용하는 경우

내장 데이터타입을 만드는 constructor 함수를 인자없이 실행하는 경우, 대부분 파이썬이 False로 인식하는 값을 반환한다.

다음 코드를 보자.

int(), float(), str(), list(), tuple(), dict(), set()
(0, 0.0, '', [], (), {}, set())

이것들을 모두 False로 인식한다.

for v in [int(), float(), str(), list(), tuple(), dict(), set()]:
    print(bool(v))
False
False
False
False
False
False
False

4.9 논리 연산

and, or, not 연산자를 True, False 값에 적용했을 때, 다음과 같이 보통 수학에서 말하는 결과를 얻을 수 있다.

print(True and True )
print(True and True)
print(not True)
print(False or True)
print(False and False)
True
True
False
True
False

앞서 이야기 했듯이 이렇게 True, False 값을 직접 파이썬 코드를 사용할 일은 거의 없다. 그리고 이를 이해하는 것이 and, or, not 연산자의 정확한 행동을 이해하는 데 바탕이 된다.

  • x or y: xfalsy이면 y를 반환하고, 그렇지 않으면 x를 반환한다.
  • x and y: xfalsy이면 x를 반환하고, 그렇지 않으면 y를 반환한다.
  • not x: xfalsy이면 True를 반환하고, 그렇지 않으면 False를 반환한다.

다음 코드와 그 결과를 위 내용과 맞춰 해석할 수 있어야 한다. 또 반환되는 값이 True 또는 False 값이 아닐 수도 있음을 주의한다.

0 or 'ksb', 1 or 'ksb'
('ksb', 1)
[] and 'ksb', [1, 2, 3] and 'ksb'
([], 'ksb')
not set(), not set([1, 2])
(True, False)

이처럼 파이썬 코드에서 이런 연산을 할 때는, True, False를 직접 사용하지 않고 객체를 대상으로 하고, 그 객체가 bool() 함수에 넣었을 때 나오는 값으로 참, 거짓을 사용하게 된다. 그러니까 if x == True:으로 코딩하지 않고, if x:라고 사용한다.

and, or 연산자는 소위 단락 평가 회로(Short-circuit evaluation)에 따라서 이뤄짐을 이해해야 한다. 예를 들어 a and b라는 코드가 있을 때 ba가 참일 때만이 평가된다. a가 거짓인 경우에는 a를 반환하고 끝내버린다. a or b라는 코드가 있을 때는 ba가 거짓일 때만 평가된다. a가 참인 경우에는 a가 반환된다. 따라서, 이런 논리 연산들은 조건식으로 대치할 수 있다.

# x or y
x = 0
y = 'ksb'
print(x or y)
if x == 0:
    result = y 
else:
    result = x
print(result)
ksb
ksb
# x and y 
x = 1
y = 'ksb'
print(x and y)
if x == 0:
    result = x 
else:
    result = y
print(result)
ksb
ksb