WeSeong Log in
← Back to posts
프로그래밍 언어

파이썬 리스트, 튜플, 딕셔너리, 함수

파이썬 리스트, 튜플, 딕셔너리, 함수

Ⅰ. 개요

파이썬의 Collections에는 대표적으로 리스트(List), 튜플(Tuple), 딕셔너리(Dictionary)가 있다. 리스트는 데이터를 순서대로 모아놓은 집합이다. 어떤 데이터형이든 가능하다. 튜플은 리스트와 거의 유사하지만 읽기만 가능하다. 딕셔너리는 Key와 Value로 나뉘는 데이터 형태를 가진 집합이다. 리스트, 튜플, 딕셔너리에서 추가, 삭제, 변경, 길이, 찾기 등의 기능을 지원하는 유용한 함수가 있다. 함수는 마치 요술상자와 같이 특정 동작을 수행한다. 파이썬에서 함수는 매개변수를 갖고 값을 반환할 수 있다. 함수에 적용할 지역변수와 전역변수 개념이 있으며 지역변수는 함수 내에서만 영향이 미치고 전역변수는 여러 함수들이 공통적으로 이용할 수 있다.


https://docs.python.org/ko/3/library/stdtypes.html#sequence-types-list-tuple-range



Ⅱ. 파이썬 리스트

파이썬에서 리스트(List)를 이해하기 위해 스택(Stack), 큐(Queue), 디큐(덱, Dequeue)의 개념을 알아야 한다. 사실 이 개념들을 정확히 이해하지 못해도 몇 번 활용하다보면 금방 사용할 수 있다. 그리고 스택, 큐, 디큐의 개념만으로 많은 내용이 필요해 여기서는 간단하게 설명하고 넘어간다. 자세히 설명한 다른 블로그나 게시글을 쉽게 찾을 수 있으니 꼭 한 번쯤 보길 권장한다.


스택은 후입선출(Last In First Out—LIFO) 구조로 데이터가 들어올 때 가장 위에 쌓이며 데이터를 불러오거나 활용할 때 가장 나중에 들어온 데이터가 먼저 나간다. 큐는 선입선출(First In First Out; FIFO)로 스택과 차이를 보이는데 먼저 들어온 데이터가 먼저 나가는 구조이다. 디큐는 선형 큐의 단점을 보완해서 데이터가 들어오고 나갈 때 순서에 상관없이 데이터를 넣고 뺄 수 있다. 먼저 들어온 데이터를 빼거나 가장 나중에 들어온 데이터를 뺄 수 있는 선택을 할 수 있다.


파이썬에서 리스트는 데이터형에 상관없이 여러 데이터를 묶어 집합을 만들 수 있다. 간단한 실생활 예로 학교에서 한 반에 여러 학생들을 모여있을 때 반은 학생을 담는 전체 그릇이고 학생은 개별적인 그릇이다. 이때 리스트는 반이 되며 학생들은 데이터가 될 수 있다. 리스트는 인덱스의 개념을 가지는데 학생이 각각 학번을 가지고 있듯 리스트로 만들어진 데이터는 각각 자신이 몇 번째 데이터인지 번호를 가진다.


※ 여기서 알아야 할 점은 인덱스 번호가 1번부터가 아닌 0번부터 시작한다는 점이다. 이는 컴퓨터가 0과 1이 기반으로 된 기계어로 프로그램을 작동시키는 점과 관련이 있다.


리스트를 생성할 때

list = []
list = [1, 2, 3, 4]

이런 형태로 생성할 수 있다. 위에 같은 경우에는 아직 데이터가 들어오지 않은 빈 리스트이며, 아래는 숫자 4개를 가진 리스트이다. 이때 아래의 리스트를 기준으로 첫 번째 데이터 인덱스는 0, 값은 1, 두 번째는 1, 2 ··· 이런 식으로 각각 인덱스 번호와 데이터가 매칭된다. 리스트는 다양한 형태를 가질 수 있는데

https://weseong.com/blog/python-outline1

이전 게시글을 참고해보면 기본적으로 정수, 실수, 문자열, 불형의 네 가지 데이터형(자료형) 모두 리스트로 만들 수 있다.


※ 파이썬의 특이한 점으로 기본 데이터형 외에도 리스트나 이후에 배울 튜플, 딕셔너리도 리스트에 하나의 데이터로서 들어갈 수 있다!


이렇게 데이터형을 가리지 않고 모두 리스트로 만들 수 있는데 이런 리스트의 데이터에 접근하는 방법은 인덱스 번호를 활용해서 직접 호출하거나 반복문을 이용할 수 있다.

list = [1, 2, 3, 4]
print(list[0])
결과 출력 ==> 1

for i in list :
    print(i, "", end="")
결과 출력 ==> 1 2 3 4

※ 여기서 또 파이썬만의 특이점으로 리스트에 덧셈과 곱셈을 할 수 있다. 두 개의 리스트를 더하거나 하나의 리스트를 특정 숫자로 곱해 반복된 데이터를 여러 개 가진 리스트로 만들 수 있다.

list1 = [1, 2, 3]
list2 = [4, 5, 6]
print(list1 + list2)
결과 출력 ==> [1, 2, 3, 4, 5, 6]

print(list1 * 2)
결과 출력 ==> [1, 2, 3, 1, 2, 3]


리스트의 데이터를 변경할 때도 인덱스 번호를 활용하면 가능하다.

list = ['a', 'b', 'c']
list[1] = 'd'
print(list)
결과 출력 ==> ['a', 'd', 'c']

list[2:3] = ['e', 'f']
print(list)
결과 출력 ==> ['a', 'd', 'e', 'f']

파이썬은 특이하게 인덱스 번호를 하나만 사용하지 않고 범위로 지정할 수 있다. 위의 예시에 앞에 숫자는 몇 번째 데이터부터 시작하는지를 지정하는 시작값이고 뒤의 숫자는 몇 번째 데이터까지 인지를 나타낸다. for 구문에서 range() 함수가 작동하는 원리와 같다. 만약 시작값을 지정하지 않는다면 첫 번째 데이터부터 지정되고 역순으로 데이터를 지정하고 싶다면 -1, -2 ··· 를 활용할 수 있다.


리스트에 데이터를 추가하거나 삭제, 개수, 정렬, 반전, 복사하는 함수들이 있다.

list = [1, 2, 3]
list.append(10)
print(list)
결과 출력 ==> [1, 2, 3, 10]

list.insert(1, 20)
print(list)
결과 출력 ==> [1, 20, 2, 3, 10]

이 두 함수는 리스트에 데이터를 추가하는 함수로 append()의 경우 맨 뒤에 데이터를 추가하며 insert()의 경우 두 가지 인자를 받는데 첫 번째 인자는 데이터를 넣을 위치, 두 번째 인자는 데이터이다.


list = [1, 2, 3, 4, 5]
del(list[0])
print(list)
결과 출력 ==> [2, 3, 4, 5]

del(list) ==> 리스트를 통째로 없앰

list = [1, 2, 3]
list.remove(2)
print(list)
결과 출력 ==> [1, 3]

list = [1, 2, 3]
list.pop()
3
print(list)
결과 출력 ==> [1, 2]

del()remove()는 리스트의 데이터를 지우는 함수로 del() 함수의 경우 특정 항목의 데이터를 지우거나 리스트를 통째로 지울 수 있다. remove()는 리스트 안에 있는 특정 데이터만을 지우며 중복된 데이터가 여러 개 있을 경우 처음 만나는 한 개의 데이터만 삭제한다. pop() 함수의 경우 리스트의 마지막 데이터를 알려주고 데이터를 삭제한다.


count() 함수는 단순한데 인자로 찾을 데이터를 넣어주면 찾는 데이터가 몇 개인지 알려준다. 리스트.count(찾을 값)을 이용하면 된다.


list = [1, 4, 2, 5, 3]
list.sort()
print(list)
결과 출력 ==> [1, 2, 3, 4, 5]

순서대로 정렬되어 있지 않은 리스트를 정렬하고 싶을 때 sort() 함수를 쓴다. 기본적으로 오름차순이며 인자로 sort(reverse=True)를 넣으면 내림차순으로 정렬된다.


※ 실제로 공부하다보면 정렬 알고리즘을 구현하는 미션을 받을 수 있다. 굉장히 어렵지만 도움이 되며, 여러 가지 정렬 방식이 있는데 방식마다 정렬에 걸리는 소요시간이 다르며 버블, 퀵 등이 있다. 공부하다 직접 조건문과 반복문을 활용해서 알고리즘을 만들어 보는 것도 재미있을 수 있으니 추천한다.


list = [20, 10, 40, 50, 30]
list.reverse()
print(list)
결과 출력 ==> [30, 50, 40, 10, 20]

list = [1, 2, 3]
copyList = list.copy()
print(copyList)
결과 출력 ==> [1, 2, 3]

reverse() 함수는 리스트의 마지막 데이터부터 역순으로 데이터 위치를 반대로 바꿔준다. copy() 함수는 리스트를 새로운 리스트로 복사한다. 위에 예시처럼 새로 복사한 리스트를 새로운 변수에 담아줄 수 있다.


※ 리스트를 복사하는 데 알아둬야 할 중요한 개념은 얕은 복사(Shallow Copy)깊은 복사(Deep Copy)가 있다. 이미 이 개념을 소개한 좋은 글들이 많아 간단히 설명하면 얕은 복사는 기존 리스트의 주소값만을 복사하기 때문에 새로 복사한 리스트나 기존 리스트가 변경되면 같이 변경된다. 깊은 복사는 새로운 메모리 공간에 기존 리스트의 실제 데이터만 복사하기 때문에 데이터 변경에 영향을 받지 않는다.


리스트의 개념이 이해되었다면 2차원 리스트를 알아두면 좋다.

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

이런 형태의 구조인데 이 리스트를 활용하기 위해 중첩된 반복문을 활용해야 한다. 여러 방면으로 활용될 수 있지만 어려운 개념으로 충분히 연습하기를 권장한다. 이 2차원 리스트의 특정 데이터를 호출할 때는 인덱스 번호가 2개가 되며 'list[0][1] =2' 이런 식으로 불러올 수 있다. 2차원 리스트를 이해하기 위해 수학의 행렬을 알면 좋다. 그리고 2차원 리스트를 잘 활용할 수 있게 되면 '테트리스' 같은 게임도 만들 수 있다. 처음에는 난이도가 높을 수 있기 때문에 책이나 다른 블로그를 참고해서 충분히 익힌 다음 도전해보길 권장한다!



Ⅲ. 파이썬 튜플

튜플(Tuple)은 리스트에서 쓰기가 안되는 읽기 전용 리스트라고 생각하면 쉽다. 그 외의 특성은 동일하며 리스트에 사용하는 함수 중 del() 함수만 가능한데 하나의 데이터만 지울 수는 없고 리스트를 통째로 지울 때만 가능하다. 나중에 상수의 개념을 공부해두면 좋은데 '처음에 변수에 할당한 값이 변하지 않고 유지되는 게 상수'이다. 튜플은 상수 리스트라고 생각하면 좋다.

tuple1 = (1, 2, 3)
tuple2 = 1, 2, 3

tuple3 = (10,)
tuple4 = 10,

튜플은 생성할 때 데이터를 정해줘야 한다. 그 데이터는 변하지 않고 계속 유지되며 생성할 때 리스트와 다르게 '()' 소괄호를 사용한다. 리스트는 '[]' 대괄호를 사용한다. 하나의 데이터만 가진 튜플을 생성하기 위해서는 위의 예시처럼 데이터 뒤에 ',(콤마)'를 붙여야 한다. 튜플은 리스트와 마찬가지로 인덱스 번호로 접근 가능하며 더하기와 곱하기가 가능하다!


※ 추가적으로 리스트와 튜플에 사용가능한 함수 중 서로 변환해주는 함수가 있다. 리스트를 튜플로 튜플을 리스트로 형태를 변환시켜주며 리스트로 변환하고 싶을 땐 list(튜플), 튜플로 변환하고 싶을 땐 tuple(리스트) 이런 식으로 사용하면 된다.



Ⅳ. 파이썬 딕셔너리

딕셔너리(Dictionary)는 '영어사전'을 생각하면 되는데 2개의 쌍이 하나의 데이터가 되며 하나는 키(Key), 다른 하나는 값(Value)이 된다.

dict = {'a':1, 'b':2, 'c':3}
dict = {1:'a', 2:'b', 3:'c'}

위의 예시처럼 딕셔너리를 생성할 땐 '{}' 중괄호를 사용한다. 키에 들어갈 데이터형은 어떤 자료형이든 상관없다. 단, 키는 중복을 가질 수 없다. 그리고 리스트와 다르게 순서가 없다.

dict = {1:'a', 2:'b'}
print(dict[1])
출력 결과 ==> a

리스트는 인덱스 번호로 데이터에 접근했지만 딕셔너리는 키로 데이터에 접근한다. 위의 예시처럼 대괄호 안에 키를 넣어주면 해당하는 값이 나온다. 딕셔너리에 데이터를 추가할 때 'dict[키] = 값'의 형태로 대입하면 되지만, 중복된 키를 넣게 되면 이전에 있던 값이 지워지고 새로 할당한 값이 대체된다. 추가할 때는 항상 키와 값이 쌍으로 대입해야 하며 리스트와 마찬가지로 del() 함수를 이용해 삭제할 수 있다.


dict = {'가수':'트와이스', '인기곡':'Cheer Up!', '데뷰':'서바이벌 식스틴'}
print(dict.keys())
결과 출력 ==> ['가수', '인기곡', '데뷰']
print(dict.values())
결과 출력 ==> ['트와이스', 'Cheer Up!', '서바이벌 식스틴']
print(dict.items())
결과 출력 ==> [('가수', '트와이스'), ('인기곡', 'Cheer Up!'), ('데뷰', '서바이벌 식스틴')]

딕셔너리에 유용한 함수로 keys(), values(), items()가 있다. keys() 함수는 딕셔너리에 키만을 리스트로 가져온다. values() 함수는 값만을 리스트로 가져오며, items() 함수는 키와 값을 튜플 형태로 리스트(2차원 리스트와 같은 형태다!)로 가져온다.


※ for 구문에서 본 적 있는 in이 있다. 이건 딕셔너리만이 아닌 조건문, 반복문 등에 모두 사용할 수 있는데 in의 반대로 not in도 있다. 간단한데 '데이터 in 리스트, 튜플, 딕셔너리, range()' 데이터가 리스트나 튜플, 딕셔너리, range() 함수로 만든 집합에 존재하는지, 속해있는지 여부를 찾아주는 것이다. not in은 in과 반대로 존재하지 않는지에 대한 여부를 찾아준다.



Ⅴ. 파이썬 함수

https://weseong.com/blog/python-outline1

함수는 이전 게시글에서 간략히 언급했는데, 매개변수를 받아 특정 동작 수행 후 return으로 값을 반환해주는 구조이다. 간단한 실생활 예로 자판기에 동전을 넣으면 음료수가 나오는 형태를 생각하면 이해하기 쉽다.

def func1() :
    동작 구조
def func2() :
    동작 구조
    return

이런 형태로 함수를 정의하며 크게 반환하는 값이 없는 경우와 있는 경우가 있다. 반환하는 값이 있는 경우 특정 변수에 해당하는 값을 넣어 다른 함수에 매개변수로 넘겨주거나 연산하거나 다양하게 활용할 수 있다. 반환하는 값이 없는 경우엔 함수안에 동작이 실행된 후 종료되며 대표적 함수로 가장 처음에 접한 print() 함수가 있다. print(), len(), count() 등 파이썬의 기본 내장 함수도 유용하게 사용하지만 실제 코딩을 해보면 사용자 정의 함수를 만들게 된다. 이 때문에 사용자마다 다른 함수가 만들어져 굉장히 다양한 함수 동작 구조가 있다.


함수는 매개변수(Parameter)를 받아서 동작할 수도 있고 받지 않고 동작할 수도 있다. 매개변수로 받은 데이터를 가공하고 처리한 후에 돌려주게 될텐데

def add(x, y) :
    result = 0
    result = x + y
    return result

기본적으로 이런 구조로 만들 수 있으며 위의 예시는 '더하기'를 수행하는 함수이다.


※ 함수까지 공부하게 되면 프로그램 만들기를 시작할 수 있다. 때문에 코드가 굉장히 길어지게 될텐데 프로그램을 작성하기에 좋은 구조를 팁으로 알려주고자 한다. '1. 함수 정의 부분이 먼저 온다. 2. 전역변수 선언 부분이 온다. 3. 프로그램 핵심 로직(메인) 코드 부분이 온다.' 전역변수는 이어서 설명하게 되고, 주석으로 동작하는 원리나 어떤 변수인지 설명하면 코드를 이해하는 데 도움이 된다. 이 구조를 반드시 따라야 하는 것은 아니지만 나중에 수백, 수천 라인의 코드가 작성될 경우 유지 보수에 굉장히 어려움을 겪기 때문에 미리 이 구조와 주석 남기는 연습을 하는 걸 권장한다.


공부의 재미를 위해 미션을 하나 남긴다. 아래의 예시는 미션을 풀어보며 예외나 구조를 고민해보며 작성했다. 앞서 얘기했듯 함수의 동작 구조는 굉장히 다양해질 수 있기 때문에 아래의 예시는 정답이 아니다. 더 좋은 코드를 작성하길 바란다!


미션: 함수를 사용한 계산기 구현

# 더하기
def add(x,y) :
	return x + y

# 빼기
def sub(x,y) :
	return x - y

# 곱하기
def multi(x,y) :
	return x * y

# 나누기
def div(x,y) :
	return x / y

# 입력
def inputNumber() :
	num = 0
	while True :
		num = input("숫자 입력 : ")
		
		if not(num.isdigit()) :
			print("숫자를 입력해주세요.")
			continue
		else :
			break

	return int(num)

# 계산입력
def inputSign() :
	sign = ''

	while True :
		sign = input("계산 입력 ( +, -, *, / ) : ")
		
		if sign not in ('+', '-', '*', '/') :
			print("제대로 된 계산을 입력해주세요.")
		else :
			break

	return sign

def control() :
	sign = inputSign()

	x = inputNumber()
	y = inputNumber()

	mark = {"+":add(x,y), "-":sub(x,y), "*":multi(x,y), "/":div(x,y)}

	result = mark[sign]

	print(f"## 계산기 : {x} {sign} {y} = {result}")

def main() :
	while True :
		print("계산기 작동")
		exit = int(input("진행1, 종료2 : "))
		
		if exit == 2 :
			break

		control()

main()


함수의 매개변수와 관련해서 파이썬만의 특이점이 있다. 매개변수의 개수는 어떤 언어든 상관없지만 파이썬에서 매개변수의 기본값을 지정할 수 있다.

def add(x, y, z = 0) :
    result = 0
    result = x + y + z
    return result

print(add(4, 5))
결과 출력 ==> 9

원래 함수에 매개변수가 3개로 지정되어 있으면 3개의 데이터를 모두 넣어줘야 하지만 위의 예시처럼 하나의 변수에 기본값이 정해져 있으면 2개의 데이터만 대입하더라도 함수가 정상작동한다. 매개변수와 관련해서 추가적으로 알아둬야할 점은 함수를 호출할 때, '이는 함수를 실행하는 것이다.' 함수의 매개변수의 개수에 맞춰 정확히 데이터를 대입해줘야하며, 매개변수의 개수가 달라지는 로직(동작 구조)이 생긴다면 별도의 함수를 생성해줘야 한다.


※ 매개변수의 데이터형은 무엇이든 가능하다!


def func1(x, y) :
    result = x + y
    return result

def func2(x, y) :
    result1 = x + y
    result2 = x - y
    return result1, result2

sum, sub = func2()

위의 예시처럼 파이썬 함수의 특징으로 특정 동작을 수행하고 반환되는 데이터가 여러 개 될 수 있다는 점이다. func1() 함수는 기본적인 '더하기' 함수라면 func2() 함수는 같은 데이터를 전달받지만 '더하기와 빼기' 둘 다 가능한 함수가 된다. 물론 반환값이 2개 이상이라면 받는 변수도 그 개수에 맞춰야 한다.


def myFunc() :
    pass

if True :
    pass

함수나 조건문, 나중에 진행할 클래스에서도 사용 가능한 키워드가 있다. 'pass'인데 의미는 프로그램 기획에 따라 구현할 클래스 혹은 함수를 선언해놓고 나중에 정의하겠다는 것이다. 주석으로 남겨도 좋지만 전체 로직을 그대로 살리면서, 업무 혹은 기능 구현 역할에 따라 배정할 때도 이용하면 유용하다.


※ 함수도 메모리 공간에 할당된다. 주소값을 가지는데 일반적으로 return되면 함수가 종료됨가 동시에 메모리 공간에서 삭제된다. 간단한 프로그램에선 신경쓰지 않아도 될 사실이지만 대규모 프로그램에선 좋은 코드를 위해 함수의 역할과 동작 방식을 고민해보는게 좋다. 특히, 반복문, 재귀함수 등으로 메모리 누수로 인해 오류가 나거나 프로그램이 종료될 때가 있다.


함수에서 마지막으로 알아둘 개념은 전역변수지역변수이다.

def func1() :
    a = 10
    print(a)

def func2() :
    print(a)

a = 20

func1()
func2()

위의 코드를 살펴보면 똑같이 변수 a를 출력시킨다. 그러나 func1() 함수에서는 a가 정의되어있지만 func2() 함수는 정의되어 있지 않다. 이 경우 두 함수를 호출해서 실행할 때 변수 a는 어떻게 출력될 것인가?

func1()
출력 결과 ==> a = 10
func2()
출력 결과 ==> a = 20

결과는 이렇게 되는데 func1() 함수는 자신의 동작 구조 안에 선언되고 정의된 a 변수를 호출하지만 그렇지 않은 func2() 함수는 전역변수로 선언된 a 변수를 참조한다. 이렇게 함수 내부에서 선언되고 정의된 변수가 지역변수이며, 여러 함수가 공통적으로 공유할 수 있는 것이 전역변수이다. 그렇다면 왜 func1() 함수는 전역변수 a를 참조하지 않은 것인가?


그 답은 지역변수가 전역변수보다 우선시 되기 때문이다. 위에서 팁으로 함수도 메모리 공간이 할당된다고 했다. 할당된 메모리 공간 안에서 구성된 기능을 실행시키는데 변수 a가 포함되어 있다. 그래서 바로 자신의 지역변수를 호출시킬 수 있지만 func2() 함수의 경우 자신의 할당된 메모리 공간 안에 변수 a가 없다. 그러나 코드는 변수 a 호출을 요구하고 있으므로 참조할 수 있는 범위에서 변수 a를 찾게 된다. 그 결과 전역변수 a를 출력시킬 수 있었다.


※ 전역변수를 함수에서 직접적으로 바로 참조할 수 있는 키워드 'global'이 있는데 global로 가져온 전역변수를 재할당할 수 있다.

def func1() :
    global a
    a = 10
    print(a)

def func2() :
    print(a)

a = 20

func1()
출력 결과 ==> a = 10
func2()
출력 결과 ==> a = 10

위의 예시와 차이점은 func1() 함수에서 global로 전역변수를 가져와 재할당해줬다는 점 뿐이다. 그 점이 중요한데 global을 선언하지 않았으면 지역변수로 작동했겠지만 전역변수로 사용하겠다고 선언해버렸기 때문에 전역변수로서 재할당되었다. 이건 주의해야 하는데 전역변수의 경우 프로그램 기능에 따라 다른 함수도 참조할 수 있기 때문에 데이터가 변하는 건 오작동을 유발할 수 있다. 그렇기 때문에 global 키워드를 사용할 때에는 유의해야 한다.



후기

오늘은 리스트, 튜플, 딕셔너리와 함수를 공부했습니다. 점점 본격적으로 파이썬을 배웠으며 구성할 수 있는 코드도 다양해지고 여러 미션을 수행하며 다양한 프로그램(계산기, 로또 등)도 만들 수 있었습니다. 그러나 예전에도 배웠었지만 처음 접하는 분들에겐 굉장히 어려운 개념이라고 생각합니다. 저도 익숙해지는데 많은 시간이 걸렸던 걸로 기억하며 이 글에선 다루지 못한 부분도 많습니다. 그래서 혹시 이 글을 읽으시는 분들에게 추천드리고 싶은 부분은 당연히 어려우니 많은 공부가 필요하고 안된다고 해서 못하는 것이 아닙니다!!🫠 저도 아직도 제대로 사용못한다고 느끼기 때문에, 이 글은 가볍게 개론을 다뤘다고 생각해주시면 감사하겠습니다.😁