상세 컨텐츠

본문 제목

[인프런 리프 2기] 08. 파이썬 중급 과정 3주차(1) 시퀀스-1,2

경험/2021 인프런리프2기

by mizu-umi 2021. 3. 25. 23:31

본문

728x90

관련글 ▼

[인프런 리프 2기] 05. 파이썬 중급 과정 2주차(3) 클래스 심화-3

[인프런 리프 2기] 06. 파이썬 중급 과정 2주차(4) 데이터 모델-1

[인프런 리프 2기] 07. 파이썬 중급 과정 2주차(5) 데이터 모델-2,3

 


 

 

파이썬 시퀀스(1)

  • 파이썬 데이터 타입 상세 분류
  • 지능형 리스트, 튜플, 딕셔너리
  • array 실습
  • 지능형 리스트 주의할 점

 

지난 과정에서는 매직 메소드와 네임드튜플에 대해 배웠다. [섹션4]에서는 이전 강의에서 언급된 파이썬에서 가장 중요한 데이터 모델 중 하나인 시퀀스에 대해서 배운다.

 

이전 강의만 보고 데이터 모델의 종류가 네가지가 다 인줄 알았는데(역시 뭐든 요약만 보면 안된다;) 파이썬 데이터 모델 페이지에서 훨씬 많은 종류의 데이터 모델을 확인할 수 있었다.

 

데이터 모델 확인 

 

3. 데이터 모델 — Python 3.9.2 문서

클래스는 특별한 이름의 메서드들을 정의함으로써 특별한 문법 (산술 연산이나 인덱싱이나 슬라이딩 같은)에 의해 시작되는 어떤 연산들을 구현할 수 있습니다. 이것이 연산자 오버 로딩 (operato

docs.python.org

 


시퀀스 타입이란?

__getitem__() 매직 메소드를 통해서 정수 인덱스(integer indices)를 사용해 효율적인 요소 접근을 지원하고 시퀀스의 길이를 돌려주는 __len__() 메소드를 정의하는 iterable(반복 가능한 자료)이다. (출처: 파이썬 용어집).

간단히 말하자면 리스트나 튜플, 딕셔너리처럼 1열로 나열 되어 있으며 어떤 번호가 붙은 타입(자료유형)을 의미한다.

 

파이썬에서의 두 종류의 데이터 유형

  • 컨테이너 (Container) : 서로 다른 자료형을 여러개 담는 데이터 유형
    • 예: list, tuple, collections.deque
  • 플랫 (Flat) : 한 종류의 자료형만 담는 데이터 유형
    • 예: str, bytes, bytearray,  array.array, memoryview

 

데이터 유형은 가변과 불변으로 다시 한번 나뉜다.

  • 가변 (Mutable) : 변화를 줄 수 있는 유형
    • 예: list, bytearray, array.array, memoryview, deque
  • 불변 (Immutable) : 변화를 줄 수 없는 유형
    • 예: tuple, str, bytes

 

데이터 유형 예제

  1. list는 어떤 유형이라고 할 수 있을까? 가변 컨테이너
  2. tuple은 어떤 유형이라고 할 수 있을까? 불변 컨테이너
  3. array는 어떤 유형이라고 할 수 있을까? 가변 플랫
  4. str은 어떤 유형이라고 할 수 있을까? 불변 플랫

 


 

지능형 리스트 (Comprhending Lists)

chars = '+)@#!%@%)'
code_list1 = []

for s in chars:
    #print(s)
    code_list1.append(ord(s))

print(code_list1)
  • ord() : () 안에 들어가는 문자의 유니코드(Unicode)를 알려주는 함수.
  • chr() : () 안에 들어가는 유니코드가 어떤 문자인지 알려주는 함수.
  • ord()로 유니코드를 뽑았다면 chr()로 유니코드를 다시 우리가 아는 값으로 출력할 수 있다.

위의 코드를 지능형 리스트로 작성하는 방법은 아래와 같다.

 

code_list2 =[ord(s) for s in chars]

지능형 리스트로 작성하는 이점은? 속도가 우세하다

 

map(), filter() 사용해서 만들기

# comprehending lists + map, filter
code_list3 = [ord(s) for s in chars if ord(s) > 40] 
# 일반적인  지능형 리스트

code_list4 = list(filter(lambda x : x > 40, map(ord, chars))) 
# filter와 map을 사용한 리스트
  • lambda(parameter: expression) :  함수가 선언되었을 때 평가되는 하나의 expression을 구성하는 익명의 인라인 함수.

결과 값은 둘다 같지만 작성하는 방식에 차이가 있다.

 


 

제너레이터 (Generator) 생성하기

제너레이터(Generator) 란?

제너레이터 반복자를 돌려주는 함수. for반복문에서 사용할 수 있는 값의 시리즈를 생산하기 위해 yield문을 포함하거나 next() 함수를 사용해서 값을 하나씩 회수할 수 있을 때를 제외하고는 보통의 함수처럼 보인다.  generator라고 하면 보통 제너레이터 함수를 말하지만 어떤 맥락에서는 제너레이터 반복자라고 하기도 한다. 의도하는 의미가 확실하지 않을 경우에는 전체 표현을 다쓰는 것이 덜 헷갈린다.(출처: 파이썬 용어집).
파이썬 제너레이터는 반복자를 생성하는 간단한 방법이라고 볼 수 있다. 간단히 말하면, 제너레이터는 반복할 수 있는 객체(반복자)를 한번에 한 값씩 돌려주는 함수이다.(출처).

요약하면, 제너레이터는 한 번에 한 개의 항목을 생성하며 최소한의 메모리를 이용해서 값을 반환하도록 돕는다dir() 함수를 이용해서 클래스를 확인했을 때 __iter__가 있는 데이터는 for-loop를 사용할 수 있다.

 

제너레이터는 tuple처럼 ( ) 를 사용하는데 지능형 리스트에서 [ ] 를 ( ) 를 바꾸면 제너레이터가 된다.

 

제너레이터를 사용하는 이유

tuple_g = [ord(s) for s in chars]

지능형 리스트를 이용해서 값을 할당할 경우, 메모리를 많이 차지하게 되어 비효율적이다.

 

tuple_k = (ord(s) for s in chars)

이렇게 지능형 리스트를 tuple로 넣어버리면 generator가 생성된다. 제너레이터는 값을 반환할 준비만 하고 있는 상태로 print(tuple_k)를 이용해 출력해보면 해당 객체가 제너레이터라고 반환한다.

 

제너레이터를 출력해보자

print(next(tuple_k))

next() 함수를 사용하면 제너레이터에서 할당된 값을 하나씩 반환해준다. 

Array 함수

import array

array_g = array.array('I', (ord(s) for s in chars))

print(array_g) 
print(type(array_g)) 
print(array_g.tolist())
  • array의 형태: array.array('typecode', initializer)
    • array에는 정해진 typecode가 있으므로 임의의 값을 typecode로써 사용할 수 없다.
  • tolist() : array를 list로 반환해주는 함수이다.

array 함수를 이용하면 훨씬 편리하게 generator를 출력할 수 있다. array가 자체함수는 아니어서 import를 이용해서 불러와야 한다.

 

제너레이터 예제

네임드 튜플

# 반에 20명, 4개의 반(A,B,C,D)

Classes = namedtuple('Classes', ['rank', 'number'])

# 그룹리스트
numbers = [str(n) for n in range(1,21)]
ranks ='A B C D'.split()

print(numbers)
print(ranks)

students  = [Classes(rank, number) for rank in ranks for number in numbers]
print(students)

제너레이터

print(('%s' % c + str(n) for c in ['A','B','C','D'] for n in range(1,21)))

 

위의 제너레이터를 하나하나 출력하고 싶으면 for-loop를 사용하면 된다.

for s in ('%s' % c + str(n) for c in ['A','B','C','D'] for n in range(1,21)):
    print(s)

 


 

지능형 리스트에서 주의할 점

# ex1
marks1 = [['~'] * 3 for _ in range(4)]

# ex2
marks2 = [['~'] * 3] * 4

위의 두 리스트를 출력해보면 아래와 같다.

 

ex1
[['~', '~', '~'], ['~', '~', '~'], ['~', '~', '~'], ['~', '~', '~']]

ex2
[['~', '~', '~'], ['~', '~', '~'], ['~', '~', '~'], ['~', '~', '~']]

출력 결과물이 동일하다. 만약 두 리스트에서 특정 값에 변화를 주면 어떨까?

 

marks1[0][1] = 'x'
marks2[0][1] = 'x'

첫번째 리스트의 두번째 값을 x로 변환하라고 작성한 다음 출력해보면 아래와 같다.

 

ex1
[['~', 'x', '~'], ['~', '~', '~'], ['~', '~', '~'], ['~', '~', '~']]

ex2
[['~', 'x', '~'], ['~', 'x', '~'], ['~', 'x', '~'], ['~', 'x', '~']]

marks1에서는 첫번째 리스트의 두번째 값만 변경된 반면, marks2에서는 모든 리스트의 두번째 값이 변경되어 있다.

 

왜? marks1의 리스트에 들어가 있는 값은 모든 리스트가 서로 다른 id를 가지는 반면 marks2의 리스트에 들어가 있는 값은 하나의 리스트를 4개로 복사한 것이기 때문에 모두 같은 id를 갖는다. 따라서 marks2의 경우에는 [1]의 값이 바뀌어도 한번에 다 바뀌어버리는 것.

 

id 값을 출력해보자

1. 처음에 내가 작성한 출력문

print(id(marks1[0]),id(marks1[1]),id(marks1[2]),id(marks1[3])) # 서로 다름
print(id(marks2[0]),id(marks2[1]),id(marks2[2]),id(marks2[3])) # 모두 동일

 

2. 강의에 소개된 출력문

print([id(i) for i in marks1])
print([id(i) for i in marks2])

 

여러개의 값을 반복해서 출력하는 것이기 때문에 지능형 리스트를 사용하면 훨씬 간단명료하게 작성할 수 있다.

 


파이썬 시퀀스 (2)

  • 튜플 고급 사용
  • Mutable(가변)
  • Immutable(불변)
  • Sort vs Sorted 실습

 


Tuple Advanced

Packing과 Unpacking

  • packing : 여러개의 객체를 하나로 묶는 것. 매개변수 앞에 * 을 붙이면 packing하게 된다.
def formula(*arg):
    n = 0
    for s in arg:
        n +=s
        print(n)

print(formula(1,2,3))

 

  • unpacking : 하나로 묶인 여러개의 객체를 푸는 것. 매개변수가 아니라 인자 앞에 *을 붙이면 unpacking하게 된다.
def formula (a,b,c):
    return a + b + c

n=[1,2,3]
print(*n)

 

예제1

print(divmod(100,9)) # divmod(x,y) x를 y로 나눈 몫과 나머지를 돌려줌
print(divmod(*(100,9))) # 100, 9라는 argument를 언패킹
print(*(divmod(100,9))) # divmod() 자체를 언패킹 한 것.

예제2

x,y,*rest = range(10) 

여기서 만약 rest앞에 *을 붙이지 않으면 too many values to unpack (expected 3)라는 error가 발생한다. 왜? range(10) 안의 값은 0~9까지라 총 10개를 할당해야하는데 x,y,rest라는 세개의 변수밖에 존재하지 않아서.

 

print(x, y, rest) ->  0, 1, [2, 3, 4, 5, 6, 7, 8, 9]

rest를 *로 packing해줬기 때문에 2~9까지를 packing해서 리스트로 출력함.

 

x, y, *rest = range(2)

여기서도 *을 붙이지 않으면 not enough values to unpack (expected 3, got 2)라는 error가 발생한다. 왜? range(2) 즉 0,1 두개의 값을 할당해야 하는데 세개의 변수만 존재하니 일치하지 않아서 오류가 생김.

 

print(x, y, rest) -> 0, 1, []

rest를 *로 packing해줬기 때문에 값은 없으나 [ ] 빈 리스트로 출력한다.

 


가변 vs 불변 (Mutable vs Immutable)

l = (2,4,8)
m = [2,4,8]

print(l, id(l))
print(m, id(m))

l 은 tuple, m은 list이기 때문에 서로 다른 id 값을 출력한다.

 

l = l * 2
m = m * 2

...

l *= 2
m *= 2

print(l, id(l)) 
print(m, id(m))

 

계속 값을 새롭게 할당해줄 경우,

  • 불변형인 tuple은 id값이 할당되면 수정이 안되므로 계속 새로운 id값이 재할당된다.
  • 가변형인 list는 특정한 순간부터 id값에 변동이 없다.

 


Sort vs Sorted

  • sorted : 해당 시퀀스 타입을 정렬 한 다음 새로운 객체를 반환한다. 원본을 그대로 두고 새로운 객체를 원할 때 사용할 수 있다.
f_list = ['orange', 'apple', 'mango', 'papaya', 'lemon','strawberry','coconut']

print('sorted -', sorted(f_list))
print('sorted -', sorted(f_list, reverse=True))
print('sorted -', sorted(f_list, key=len))
print('sorted -', sorted(f_list, key=lambda x: x[-1]))
print('sorted -', sorted(f_list, key=lambda x: x[-1], reverse=True))

print(f_list)

여기서 print(f_list)를 작성하고 출력하면 f_list에 할당한 원본값이 그대로 반영된다.

 

  • sort : 해당 시퀀스 타입을 정렬한 다음 객체를 직접 변경한다. 원본이 변경되어도 괜찮을 때 사용할 수 있다.
print('sort-', f_list.sort(), f_list) 

print(f_list) 

print('sort-', f_list.sort(reverse=True), f_list)
print('sort-', f_list.sort(key=len), f_list)
print('sort-', f_list.sort(key=str.lower), f_list)
print('sort-', f_list.sort(key=lambda x: x[-1]), f_list)
print('sort-', f_list.sort(key=lambda x: x[-1], reverse=True), f_list)

중간에 한번 print(f_list)를 해서 출력하면 기존의 f_list가 아닌 sort로 인해 변경된 객체가 출력된다.

 

  • 정렬과 관련된 함수들로 reverse, key=len, key=str.Lower, key=func.. 등등의 정렬 방식이 있다.

 


List vs Array 적합한 사용법

  • 리스트 : 융통성. 컨테이너 타입이기 때문에 다양한 데이터 타입을 담을 수 있음. 범용적.
  • 어레이 : 숫자 기반일 때. 배열(리스트와 거의 호환됨). 

뱀발

l = (2,4,8)
m = [2,4,8]

# 얘네는 값이 다름
print(l, id(l))
print(m, id(m))

# 얘네는 값이 같음
print(l, id(l[0]))
print(m, id(m[0]))

# why?
print(l[0]) # 이게 2고
print(m[0]) # 얘도 2임

print(bool(l[0]==m[0])) 

 


< 강의 출처 - 인프런 우리를 위한 프로그래밍 파이썬 중급 (Inflearn Original) 

728x90
반응형

관련글 더보기

댓글 영역