기타/공부노트

[파이썬 / python]챕터5: 튜플, 리스트, 앨리어싱, 변경가능, 복제 (Tuples, Lists, Aliasing, Mutability, Cloning)

코드아키택트 2021. 1. 17. 20:57
반응형

※ MIT 6-0001-fall-2016 강의내용 정리입니다. 원 내용은 MIT Opencourseware에서 찾아 보실 수 있습니다.

※ 설명용 그림들은 아이폰 크기를 기준으로 만들었습니다. PC로 보시는 분들은 브라우저 가로 사이즈를 줄이시면 한눈에 보실 수 있습니다.

튜플

 

 

튜플의 정의 및 특성

an ordered sequence of elements, can mix element types
cannot change element values, immutable
represented with parentheses

요소의 정렬된 배열이며, 여러가지 타입을 섞을 수 있다
요소들의 값을 바꿀 수 없다.
()로 표현한다

 

예시 코드

te = () -> 비어있는 tuple 생성
t = (2,"mit",3)
print("The value of t[0] is " + str(t[0]))
print("The value of t[1:2] is " + str(t[1:2]))
print("The value of t[1:3] is " + str(t[1:3]))
print("The value of len(t) is " + str(len(t)))
t[1] = 4

 

결과

The value of t[0] is 2
The value of t[1:2] is ('mit',)
The value of t[1:3] is ('mit', 3)
The value of len(t) is 3
TypeError: 'tuple' object does not support item assignment

 2번째 결과값을 보면 ('mit',)로 나오는데, 이것은 1개의 Element만 가진 튜플은 끝에 ","가 나오도록 되어있기 때문입니다.

 

 마지막에 에러가 나는 이유는 튜플은 변경불가능(Immutable)한 속성을 가지고 있기 때문입니다.

 

 

튜플 활용

 

손쉽게 값을 교환(swap)할 수 있다.

튜플을 사용하지 않을때

x = 5
y = 7
print("The values of x, y are: " + str(x) + ", " + str(y)) 
-> The values of x, y are: 5, 7


temp = x
x = y
y = temp
print("The swapped values of x, y are: " + str(x) + ", " + str(y))
-> The swapped values of x, y are: 7, 5

 임시 값에 내용을 저장한 후, 다시 꺼내서 써야합니다.

 

튜플을 사용할때

x = 5
y = 7
print("The values of x, y are: " + str(x) + ", " + str(y))
-> The values of x, y are: 5, 7

(x,y) = (y,x)
print("The swapped values of x, y are: " + str(x) + ", " + str(y))
-> The swapped values of x, y are: 7, 5

a = 5
b = 7
c = 12
print("The values of a, b, c are: " + str(a) + ", " + str(b)+ ", " + str(c))
-> The values of a, b, c are: 5, 7, 12

(a,b,c) = (c,b,a)
print("The swapped values of a, b, c are: " + str(a) + ", " + str(b)+ ", " + str(c))
->The swapped values of a, b, c are: 12, 7, 5

 임시값에 저장한 후 할필요없이 바로 바꿀 수 있습니다. 약간의 응용으로 3개에 대해서도 해보았고, 잘 작동하는 것을 볼 수 있었습니다. 이를 응용해보면 n개의 데이터에대한 swap도 가능할 것입니다. 물론 데이터가 너무나 많다면 루프를 돌리는게 더 나을것입니다.

 

기능(Function)에서 두개 이상의 값을 반환할 수 있음.

def quotient_and_remainder(x,y):
    q = x // y #quotient, 몫
    r = x % y #remainder, 나머지
    return (q,r)

(quot, rem) = quotient_and_remainder(4,5)

print("The quotient and remainder 4 divided by 5 are " + str(quot) + ", " + str(rem))
-> The quotient and remainder 4 divided by 5 are 0, 4

 

튜플 다루기 : 루프 돌리기

루프 돌릴 자료구조 이해

 

aTuple((),(),()) -> Tuple in Tuple 구조

aTuple(( int , strings ),( int , strings ),( int , strings )) -> Tuple in Tuple 구조이며 가장 안쪽 Tuple은 순서대로  int (상수) 값과  strings  (글자) 값을 가지고 있음.

def get_data(aTuple):
    nums = () # empty tuple
    words = () # where unique words will be contained
    for t in aTuple:
        nums = nums + (t[0],) # get the integer of tuple in tuple
        if t[1] not in words:
            words = words + (t[1],) # if the string in tuple in tuple is a unique word, add to tuple 'words'
    min_n = min(nums)
    max_n = max(nums)
    unique_words = len(words)
    return (min_n, max_n, unique_words)

test = ((1,"a"),(2, "b"),
        (1,"a"),(7,"b"))
(min, max, uniqueWords) = get_data(test)
print("min:",min,"max:",max,"uniqueWords:",uniqueWords)

 

결과값

min: 1 max: 7 uniqueWords: 2

 

 

테일러 스위프트 데이터로 해보기

tswift = ((2014,"Katy"),
          (2014, "Harry"),
          (2012,"Jake"), 
          (2010,"Taylor"), 
          (2008,"Joe"))    
(min_year, max_year, num_people) = get_data(tswift)
print("From", min_year, "to", max_year, \
        "Taylor Swift wrote songs about", num_people, "people!")

 

결과값

From 2008 to 2014 Taylor Swift wrote songs about 5 people!

 최대, 최소값 그리고 고유값의 갯수를 사용해서 정보를 얻어냄. 문법중 print 부분에서 string, variable, string 구조로 썼을때 그 결과가 string string string으로 나오는 부분은 유용하게 쓸 수 있을 듯 하다.

 

 

 

리스트

 

 

정의 및 특성

ordered sequence of information, accessible by index
a list is denoted by square brackets, [ ]
a list contains elements
• usually homogeneous (ie, all integers)
• can contain mixed types (not common)
list elements can be changed so a list is mutable

정보의 정렬된 배열이며, index로 접근함.
[ ](대괄호)로 표시함
요소들을 포함함
- 일반적으로 한가지 타입
- 섞을수도 있음(흔치 않음)
리스트의 요소들은 바뀔 수 있음. 그러므로 변경가능함(mutable)

 

 

인덱스와 순서

 

예시코드

a_list = []
L = [2, 'a', 4, [1,2]]
print("len(L):",len(L))
print("L[0]:",L[0])
print("L[2]+1:",L[2]+1)
print("L[3]:",L[3])
i = 2
print("L[i-1]:",L[i-1])
L[4]

 

결과값

len(L): 4
L[0]: 2
L[2]+1: 5
L[3]: [1, 2]
L[i-1]: a
IndexError: list index out of range

 다들 알겠지만 index는 0부터 시작한다. 위의 리스트는 인덱스가 3까지존재하는데 4를 넣었기 때문에 에러가 난다.

 

 

요소 바꾸기

 

예시코드

L = [2, 1, 3]
L[1] = 5
print(L)

 

결과값

[2, 5, 3]

 

 

리스트 루프 돌리기

- 리스트의 값 더하기

- 일반적인 패턴

 

인덱스를 이용해서 요소 하나씩 가져오는방법

total = 0
	for i in range(len(L)):
		total += L[i]
	print total

※인덱스 범위는 0~ len(L) -1

range(n)0~ n-1을 포함함.

 

인덱스 이용없이 바로 루프

total = 0
	for i in L:
		total += i
	print total

 

응용코드

def sum_elem_method1(L):
  total = 0 
  for i in range(len(L)): 
      total += L[i] 
  return total
  
def sum_elem_method2(L):
    total = 0 
    for i in L: 
        total += i 
    return total

print(sum_elem_method1([1,2,3,4]))
print(sum_elem_method2([1,2,3,4]))

 

결과값

10
10

둘다 결과는 같음.

 

리스트 기능(Operation) - 리스트에 더하기

  • L.append(element)를 이용하여, 리스트 끝에 요소를 더함
  • 더하게 되면, 리스트를 변경시킨다
    • L = [2, 1, 3]
    • L.append(5) -> L은 [2, 1, 3, 5]가 된다

▶ 점(.)의 의미는?

  • 파이썬의 모든것은 object이며, 리스트 또한 object이다.
  • object는 다음과 같응ㄴ 것을 가진다
    • data
    • methods
    • functions
  • 이러한 정보들을 object_name.do_something()구조를 통해 접근한다
  • 자세한 내용은 추후에 더 배운다.
  • `+`를 이용해서 리스트를 합칠 수 있으며, 결과로서 새로운 리스트를 만들어낸다.
  • L.extend(some_list)를 이용하여 기존 리스트를 변경할 수 있다.

코드 예시

L1 = [2, 1, 3]
L2 = [4, 5, 6]
L3 = L1 + L2
print("L1",L1,"L2",L2,"L3",L3)

L1.extend([0, 6])
print("L1",L1)

 

결과값

L1 [2, 1, 3] L2 [4, 5, 6] L3 [2, 1, 3, 4, 5, 6]
L1 [2, 1, 3, 0, 6]

-> 다시 요약하면 L1+L2는 그 결과값을 만들어 내지만 기존 리스트를 변경시키진 않음. 반대로 L.extend(some_list)구조를 사용하면 기존 리스트가 변경됨.

 

 

리스트 기능(Operation) - 리스트에서 제거하기

  • 특정 인덱스에 있는 요소를 del(L[index])를 이용해 제거함. (결과값을 바로 return하진 않음)

  • L.pop()을 이용해 마지막 요소를 제거하고, 그 요소값을 반환

  • 특정한 요소L.remove(element)를 이용해 제거함

    • 요소를 찾아서 지움

    • 만약 같은 요소가 여러번 있으면, 처음 요소만 제거

    • 리스트에 요소가 없으면 에러 발생

 

예시코드

L = [2,1,3,6,3,7,0]
print("L.remove(2)",L.remove(2))
print("L",L)
print("L.remove(3)",L.remove(3))
print("L",L)
del(L[1])
print("L",L)
print("L.pop()",L.pop())
print("L",L)

 

결과값

L.remove(2) None
L [1, 3, 6, 3, 7, 0]
L.remove(3) None
L [1, 6, 3, 7, 0]
L [1, 3, 7, 0]
L.pop() 0
L [1, 3, 7]

 

 

리스트를 스트링으로 바꾸고 다시 리스트로

  • list(s)로 string을 리스트로 바꾸고 string의 각 문자가 리스트의 요소가 됨.

  • s.split()하나의 문자를 기준으로 string을 나눔. 아무런 값을 선언하지 않으면 공백을 기준으로 나눔.

  • ''.join(L)을 이용해 리스트의 모든 문자들을 string으로 바꿈. ''사이에 각 요소 사이에 넣을 문자를 입력 가능

     

     

 

코드예시

s = "I<3 cs"
print("list(s)",list(s))
print("s",s)
print("s.split('<')",s.split('<'))
print("s",s)

L = ['a','b','c']
print("''.join(L)",''.join(L))
print("'_'.join(L)",'_'.join(L))

 

실행결과

list(s) ['I', '<', '3', ' ', 'c', 's']
s I<3 cs
s.split('<') ['I', '3 cs']
s I<3 cs
''.join(L) abc
'_'.join(L) a_b_c

-> 위의 operation을 행하면 각 대응되는 값을 return함. 원본 데이터인 s는 변경되지 않는 것을 볼 수 있음.

 

 

기타 기능들

 

예시코드

L=[9,6,0,3]
print("sorted(L)",sorted(L))
print("L",L)
print("L.sort()",L.sort())
print("L",L)
print("L.reverse()",L.reverse())
print("L",L)

 

결과

sorted(L) [0, 3, 6, 9]
L [9, 6, 0, 3]
L.sort() None
L [0, 3, 6, 9]
L.reverse() None
L [9, 6, 3, 0]

-> sort()와 reverse()는 리스트를 변경시키지만 return은 하지 않음. sorted(L)의 경우 정렬된 리스트를 반환하지만, 리스트를 변경시키진 않음.

 

 

 


변경, 앨리어싱, 복제(MUTATION, ALIASING, CLONING)

 

 

메모리 안의 리스트

  • 리스트는 변경 가능하다

  • 변경 불가능한 타입과 다르게 작동한다

  • 메모리 안의 object이다

  • 변수이름은 object를 가르킨다

  • 같은 object를 가르키는 모든 변수들은 모두 영향을 받는다.

  • 가장 주의해야 할점은 이러한 특성으로 생기는 부작용

 

 

비유

Justin Bieber singer rich troublemaker
The Bieb singer rich troublemaker
JBeebs singer rich troublemaker
뜨또 singer rich troublemaker

뜨또의 유래

  • 사람(비버)의 속성

    • Singer, rich

  • 많은 별명이 있으며, 모든 별명은 저스틴 비버를 가르킴

  • 하나의 별명에 새로운 속성을 더한다면, 모든 별명에 영향을 받음.

-> 즉, 하나의 사람은 여러가지 별명을 가지고 있으며 어떤 별명을 통해서라도 그 사람에 새로운 속성을 부여하면 (짠순이, 키다리 등등) 모든 별명이 영향을 받는다.

-> 기술적으론 모든 변수들이 하나의 Object를 가르키고 있는데, Object가 어떤 변수를 통해서라도 변경되면 모든 변수가 영향을 받는다.

 

 

앨리어싱

명쾌하게 설명해놨다.

  • hot은 warm의 alias(가명)이다. -> 하나를 바꾸면 다른 하나가 바뀐다.

  • append()는 부작용을 가지고 있다.

앨리어싱으로 인해 hot만 변경해도 warm도 변하는 경우.

 

예제 코드

warm = ['red', 'yellow', 'orange']
hot = warm
hot.append('pink')
print(hot)
print(warm)

 

결과값

['red', 'yellow', 'orange', 'pink']
['red', 'yellow', 'orange', 'pink']

-> Aliasing으로 생길 수 있는 문제점을 보여줌

 

 

리스트 복제하기

Clone을 통해 기존 리스트에 영향을 주지 않으면서 원하는 계산을 해냄.

예제코드

cool = ['blue', 'green', 'grey']
chill = cool[:]
chill.append('black')
print("chill",chill)
print("cool",cool)

 

결과값

chill ['blue', 'green', 'grey', 'black']
cool ['blue', 'green', 'grey']

 

 

리스트 정렬하기

  • sort()는 리스트를 정렬시켜 변화시키지만, return은 하지 않음

  • sorted()는 리스트를 변화시키지 않지만, 정렬된 값을 return함

L.sort()의 경우

 

 

예제코드

warm = ['red', 'yellow', 'orange']
sortedwarm = warm.sort()
print("warm",warm)
print("sortedwarm",sortedwarm)

 

결과값

warm ['orange', 'red', 'yellow']
sortedwarm None

 

 

sorted(L)을 사용하는 경우

 

예제코드

cool = ['grey', 'green', 'blue']
sortedcool = sorted(cool)
print("cool",cool)
print("sortedcool",sortedcool)

 

결과값

cool ['grey', 'green', 'blue']
sortedcool ['blue', 'green', 'grey']

 

 

 


Nested List

  • 앨리어싱으로 인한 부작용은 여전함.

Nested List일때 부작용

 

예제 코드

warm = ['yellow', 'orange']
hot = ['red']
brightcolors = [warm]
brightcolors.append(hot)
print("brightcolors",brightcolors)
hot.append('pink')
print("hot",hot)
print("brightcolors",brightcolors)

 

결과값

brightcolors [['yellow', 'orange'], ['red']]
hot ['red', 'pink']
brightcolors [['yellow', 'orange'], ['red', 'pink']]

-> [[],[]]의 구조로 List in List가 만들어 진 것을 볼 수 있음. 이는 예제 코드의 3번째 줄 [warm]의 영향

 

 

예제 코드: 3번쨰줄 변경

warm = ['yellow', 'orange']
hot = ['red']
brightcolors = warm
brightcolors.append(hot)
print("brightcolors",brightcolors)
hot.append('pink')
print("hot",hot)
print("brightcolors",brightcolors)

 

결과값

brightcolors ['yellow', 'orange', ['red']]
hot ['red', 'pink']
brightcolors ['yellow', 'orange', ['red', 'pink']]

-> 마지막을 보면 [a,b,[list]]의 구조인 것을 볼 수 있음.

 

 

 


마무리 : 뮤테이션과 Loop

> 리스트를 루프할때 mutation을 피해라

 

잘못된 코드

def remove_dups(L1, L2):
	for e in L1:
		if e in L2:
			L1.remove(e)

L1 = [1, 2, 3, 4]
L2 = [1, 2, 5, 6]
remove_dups(L1,L2)

print("L1",L1,"L2",L2)

 

결과값

L1 [2, 3, 4] L2 [1, 2, 5, 6]

-> 제대로 된다면 L1 [3, 4]가 나와야함.

 

이유 알아보기

인덱스가 0일때 비교를 통해 1이 삭제됨. 그 결과로 리스트 안의 아이템들이 한칸씩 당겨짐. 인덱스가 1일때, 당겨진 순서를 기준으로 하기 때문에 2는 건너뛰게됨. 그래서 문제가 됨.

 

올바른 코드

def remove_dups_new(L1, L2):
    L1_copy = L1[:]
    for e in L1_copy:
        if e in L2:
            L1.remove(e)

L1 = [1, 2, 3, 4]
L2 = [1, 2, 5, 6]
remove_dups_new(L1, L2)
print("L1",L1,"L2",L2)

 

결과값

L1 [3, 4] L2 [1, 2, 5, 6]

 

반응형