Python - list, tuple, set, dictionary, 그리고 Comprehension

solee·2022년 1월 29일
0

Python

목록 보기
2/16

자바를 공부해 온 사람으로서 어색했던 파이썬 문법 중 하나가 list다. 정확히는 배열! 매번 배열의 크기 때문에 머리를 많이 싸맸는데, 파이썬이 참 사람을 편안하게 해 준다. 땡큐 파이썬!

여담인데, 문득 파이썬이 뱀은 뱀인데 무슨 뱀인지 궁금해져서 구글링을 좀 해봤으나 무슨 검색어를 함께 써도 코딩에 관련된 결과가 나와서 조금 재미있었다. python snake라고 쳐도 game 이라는 검색어가 함께 뜬다거나... animal같은 단어를 넣으니 멀쩡한 비단뱀이 나왔지만 picture을 검색하니 picture to text가 뜬다거나... 역시 이름을 지을 때 이미 있는 단어를 그대로 쓰면 검색이 어렵다. 파이썬은 원래 단어를 잡아먹어 버렸지만!




오늘은 배열, 즉 리스트, 딕셔너리, 튜플, 그리고 세트에 대해 포스팅해보려 한다.

파이썬의 리스트는 참 착하다. 자료형도 다양하게 넣을 수 있고, 크기도 자유롭고(물론 이건 자바의 리스트도 그렇지만...), 애가 똑똑하다. 종류가 다양한 고로, 알아보기 쉽게 분류하자면 이렇다:

간단한 형태 비교

리스트 List 튜플 Tuple 세트 Set 딕셔너리 Dictionary
빈 자료형
선언
l = [] t = () s = set() d = {}
형태 l = [1, 2, "a"] t = (1, 2, "a") s = {1, 2, "a"} d = {1:"a", 2:"b", "three":"c"}

딱 보면 이런 생각이 든다.
세트는 왜 빈 자료형을 선언할 때 혼자 s = set()이야?
딕셔너리는 왜 혼자 d = {1:"a", 2:"b", "three":"c"} 이야?

추가하자면, 다른 배열들도 l = list()처럼 선언할 수 있다. 하지만 set는 딕셔너리와 같이 {} 중괄호를 사용하므로 s = {} 를 사용할 수 없다.(그냥 선언하면 딕셔너리가 된다)

나머지는 비슷해 보이는데?
물론 다르다. 차이점도 비교해 보자면 이렇다.

간단한 기능 비교

리스트 List 튜플 Tuple 세트 Set 딕셔너리 Dictionary
형태 l = [1, 2, "a"] t = (1, 2, "a") s = {1, 2, "a"} d = {1:"a", 2:"b", "three":"c"}
값 추가 l.append("l") 불가 s.add("s") d = {"four":"d"}
d["four"] = "d"
값 삭제 l.remove("a") 불가 s.remove("a") del d["a"]
형변환 list(array) tuple(array) set(array) 불가
인덱싱 가능 가능 불가능 불가능
특징 수정 가능 수정 불가능 수정 가능
중복 불가
수정 가능
키 : 값 페어

헷갈리는 경우가 있다면, 이 표가 도움이 되기를 바란다.

  • 먼저 리스트는 가장 기본적인 배열로, 수정, 추가, 삭제 등이 가능하며 append()와 remove()로 추가 및 삭제할 수 있다. 대괄호[]를 사용한다
  • 튜플은 한번 작성하면 수정도 삭제도 추가도 불가능한 배열이다. 소괄호()를 사용한다.
  • 세트는 중복을 허용하지 않는다. 추가할 때 append()가 아닌 add()를 사용하는 점에 유의한다.

  • 리스트와 튜플은 순서가 존재한다. 그러므로 슬라이싱도 가능하며 n번째 요소를 리턴하라는 명령을 내릴 수 있다. t[2]라면 인덱스로 2번째 값을 리턴할 것이다.
  • 세트와 딕셔너리에는 순서가 존재하지 않는다.

딕셔너리는 키를 사용해야 하므로 이해했으나, 왜 세트는 인덱스를 사용할 수 없는 것일까? 세트는 다른 배열에는 없는 특별한 기능을 가지고 있다.


세트

세트set는 수학에서 집합이다. 그러므로, 집합이 가지는 성질을 가지고 있다. 바로바로... 교집합, 합집합, 차집합이 그것이다.

세트 a와 b가 있다고 하자.

  • 교집합 = 두 세트에서 중복되는 값
a & b
a.intersection(b)

- 합집합 = 두 세트를 합한 값
a | b
a.union(b)

- 차집합 = 두 세트에서 중복되지 않는 값
a - b
a.difference(b)

wow~ 아주 유용한 기능이므로, 안타까울 수 있다. 어? 나는 이 기능이 필요한데, 내가 가 자료형은 리스트다! 걱정하지 않아도 된다.

wholeList = list(set(listA) | set(listB))

간단하다. 각 리스트를 세트로 형변환한 후, 합집합이나 교집합 등의 기능을 이용하고 다시 리스트로 형변환하면 된다.



그리고 딕셔너리.

딕셔너리는 다른 배열과 다르게 키와 값이 한 쌍을 이루는 것을 확인할 수 있다. 이는 자바의 map과 같다. 파이썬에서 map을 input()에 사용할 때의 당황이란!

데이터를 추가할 때에는 한 쌍을 통째로 추가하거나, 딕셔너리명[키]=값 형태를 사용해 추가할 수 있다. 삭제할 때에는 del 딕셔너리명[키] 명령어를 사용한다.

딕셔너리의 "키"는 중복을 허용하지 않기 때문에, 중복된 값을 추가하려고 하면 기존에 같은 키를 사용하는 데이터가 업데이트되어 이전의 값이 사라지고 새로운 값이 덮어씌워진다.


딕셔너리로는 키를 가지고 값을 얻는다. 두 가지 방법이 있는데 형태는 다음과 같다.

딕셔너리명[키]딕셔너리명.get(키)이다.
가장 큰 차이점은 오류다. 전자, 즉 a[1]를 사용할 경우, 존재하지 않는 키를 입력하면 오류가 발생하고 프로그램이 종료된다. a.get(1)을 사용하면 None이 리턴된다.

그래도 딕셔너리명[키]를 사용하고 싶다면?
a.get("nonExist", "alternative")으로 두 번째 인자를 주면 된다. 찾으려는 키가 존재하지 않을 경우 두 번째 인자를 리턴한다.


딕셔너리의 키를, 값을 전부 보고 싶다면 어떻게 하면 될까?

만약 a라는 딕셔너리의 키를 모아서 보고 싶다면, a.keys()를 사용하면 된다. dict_keys([1, 2, 3])라는 결과가 나온다. values도 마찬가지다.

둘 다를 보고 싶다면? a.items()다. dict_items([(1, 'a'), (2, 'b'), (3, 'c')])라는 결과가 나온다. 이런 결과물들은 자료형이 list가 아니라 dict_keys, dict_values, dict_items 그 자체다.




컴프리헨션

백준 알고리즘 문제를 풀던 때에, 나는 딕셔너리의 키가 아니라 값을 가지고 키를 구하고 싶었다. 정말 열심히 찾아봤는데, 이런 것을 찾았다.

for i in range(len(phN)):
    for v in (dial.get(key) for key in dial.keys() if phN[i] in key): 
        time += v

print(time)
+ 딕셔너리의 값으로 키를 찾고 싶은 분들, 위의 코드가 도움이 되셨음 좋겠어요^*^

일단 변형해서 썼더니 결과는 나왔는데, 도무지 이게 뭔지 알 수 없었다. 그러니까, 의미는 일단 대충 알아먹었으나 꼭 영어 문장을 단어만 가지고 대충 해석한 것 같아서 이 문장을 가지고 다른 문장을 만들라고 한다면 만들 수가 없었다.

엥? 내가 배운 for문이랑 너무 다른데? for 앞에 있는 건 뭐야? 뒤에 if 아에 있는 건? if절은 어디에 어떤 역할을 하는 거야?
심지어 이런 문장을 for문 외에 뭐라고 검색해야하는지도 모르겠어서(구글링해서 이 코드를 찾았던 글도 딕셔너리의 값으로 키를 찾고 싶다는 질문글에 코드만 달린 것이어서 힌트가 없었다) 애꿎은 for문만 찾다가 말았는데, 예상치 못하게 list와 dictionary를 검색하다가 찾았다.


  • List Comprehension (LC)
  • Set Comprehension (SC)
  • Dict Comprehension (DC)
  • Generator Expression (GE)

위에서 찾았던 문장은 이 중 Generator Expression인데, 나중에 따로 빼서 소개하고 오늘은 List, Set, Dictionary에 관련된 것만 정리해 보겠다.


List Comprehension

딱 보면 "뭐야?"처럼 생겼다. 그런데 제대로 읽어보면 아주 짧게 표현한 for문이자 리스트 생성문이다.

a = [i*2 for i in range(5)]
  1. 코드를 []로 감쌌으므로 리스트가 된다.
  2. 0부터 4까지 총 5개의 숫자인 i를 가진 for문을 볼 수 있을 것이다.
  3. 그 i에 각각 * 2를 한 것을 리스트에 추가한다.

그러므로 위의 문장은 이런 뜻이 된다:

0부터 4까지의 값에 각각 2를 곱해 리스트를 생성한다.

결과는 이렇다:

[0, 2, 4, 6, 8]

짜잔!
이 내용을 평범한 for문으로 만든다면 아래처럼 되겠지.

a = []
for i in range(5):
  a.append(i*2)

세 문장을 한 문장으로 만들 수 있는 것이다!
list comprehension을 풀어서 표현하자면 다음과 같다.

[ 변수를 사용하는 코드 for 변수(보통 i) in 범위 ]

여기에 하나 더 사용할 수 있다. 바로 if문이다.

a = [i for i in range(10) if i%2==1]

알아볼 수 있겠는가?

0부터 9까지의 값 중 i를 2로 나눈 나머지가 1인, 즉 홀수인 수로 리스트를 생성한다.

당연히 a를 출력하면 [1, 3, 5, 7, 9]가 된다.

그러니 리스트 컴프리헨션을 다시 풀어서 설명하면,

[ 리스트에 들어가게 될 i를 활용한 코드 for i(다른 변수도 물론 가능)
  in 범위 [if 리스트에 들어갈 i의 조건] ]

마지막의 if문은 optional한 []다. 그리고 범위가 아닌 리스트에 들어갈 i의 조건이 된다.

if 조건을 여러개 사용할 수도 있다.

a = [i for i in range(100) if i%5==0 if i%7==0]

0부터 100 중에 5의 배수 그리고 7의 배수인 수로 리스트를 생성한다.

그냥 if문을 중첩해 주는 것으로 and연산자가 적용된다.
or 연산자를 사용하고 싶다면, 여러 if문이 아니라 한 if문에서 and나 or 연산자를 사용해 조건을 사용해야 한다.

또한 숫자뿐만아니라 문자열 등으로도 사용할 수 있다. 주어진 문자열의 한 글자씩 리스트에 집어넣을 때 등등 다양하게 사용할 수 있겠지.



Set Comprehension

세트 컴프리헨션은 조금 허망하다. 리스트 컴프리헨션과 완전히 동일하게 사용되지만, 겉을 감싸는 [] 대신 {} 중괄호를 사용한다.



Dictionary Comprehension

리스트 컴프리헨션을 이해했다면, 딕셔너리도 비슷한 원리로 작동한다는 것을 이해할 수 있을 것이다.
다만 딕셔너리는 키와 값을 쌍으로 가지는 만큼, 형태가 조금 다르다.

{ 키 : 값 for 변수 k, v in 범위 [if 딕셔너리에 들어갈 키, 값의 조건] }

맨 앞의 키 : 값에 주의해야 한다.

리스트, 세트와 마찬가지로 [if 조건문]은 optional하며, 변수 둘을 동시에 가진다. 또 for과 in 사이의 변수 k, v도 달라질 수 있다. 이미 있는 리스트를 zip()을 이용해 딕셔너리로 만들 때에도 많이 사용되는 듯하다.

a = ["cat", "dog", "hamster"]
b = ["4kg", "9kg", "35g"]

dic = { k : v for k, v in zip(a, b) }

동물과 그 무게를 딕셔너리로 만든다면 이런 방식으로 만들 수 있다.
꼭 변수가 두 개가 아니어도 된다.

a = [1, 2, 3, 4, 5]
dic = { k : k*2 for k in a if k%2==1}

키는 k이고 값은 k*2다. 그 k를 a라는 범위에서 하나씩 가져온다.
그리고 그 값이 홀수일 때에만 딕셔너리에 추가한다.

k에 대해서 if문이 검증하므로 결과는 이렇다: {1: 2, 3: 6, 5: 10}
만약 값을 k*2가 아니라 다른 리스트를 이용해도 1, 3, 5가 키인 것으로만 생성된다는 결과는 같다. k에 대해서만 if문이 검증하기 때문이다.


마지막으로, 찾다 보니 앗~! 하게 되는 식이 하나 더 있다.
a = {'cat': '4kg', 'dog': '9kg', 'hamster': '35g'}
b = { v : k for k, v in a.items()}

  스윗~

v : k에서 볼 수 있듯이 a.items()로 딕셔너리 a의 모든 항목을 가지고 키와 값을 바꿔서 저장한 것이다. 값을 가지고 키를 꺼낼 일이 많다면 아예 이렇게 만들어 놓고 사용하기 좋겠다.





마치며

모르고 볼 때에는 뭐야... 이걸로 뭘 어떻게 하라고... 같은 심정이었지만, 제대로 읽어보니 우와!가 된다. 나는 우와!가 참 좋다. 코드가 짧고 예쁘고 보기 쉽고 간결하고 그리고 효율적이었으면 좋겠다. 내 코드가 우와!했으면 좋겠다.
계속 공부해야지!

profile
DA DA DA

0개의 댓글