2020년 2학기 파이썬 첫 스터디 멘토링

동아리 내 스터디에 멘토로 참가했는데, 첫 스터디에서 다룬 내용들을 정리해봤다. 어쨰 그룹 과외가 된 느낌이지만... 혼자서 공부하기엔 어려운 개념들이 있어서 설명해줬다.

아래는 시간상 듣지 못한 사람들을 위한 수업 정리글이다.


중급반이므로 생활코딩 사이트 ( https://opentutorials.org/course/1750/9620 ) 기준으로 조건문과 그 위쪽은 생략하기로 했습니다.

오늘 스터디에서는 타입, 리스트, dict 타입, 반복자(Iterator)와 객체 지향의 기본적인 개념에 대해서 다루었는데요, list 와 dict는 파이썬의 내장 자료형인데, 타입에 대해선 간단하게 언급만 했고요, 그 다움에 list / dict의 문법과 특징에 대해서 짧게 설명했습니다.

우선 list의 기본 개념은 학교 수업 덕분에 다들 알고 계셔서 넘어갔습니다.

list의 특징으로 다룬 건 원소의 타입에 관한 건데요, 파이썬에서의 list는 원소의 타입이 서로 달라도 됩니다. 예를 들어

l = ['str', 1, None, [1]]

같은 경우 원소의 타입은 첫번째부터 차례대로 문자열, 숫자, None (특수 타입), list입니다. C 등의 언어와 다른 점이라 헷갈릴 가능성이 있을 것 같아서요.

학교 수업이 dict에 대해서 많이 가르치지 않기에 dict에 대해선 좀 더 설명했는데요,

var = {}

이런 식으로 빈 dict를 초기화할 수 있고,

var = {
    'a': 'b'
}
var['a'] = 'c'
print(var['a'] )

이런 식으로 dict를 초기화하거나 접근, 수정할 수 있습니다.

dict의 키가 숫자일 수도 있는데요, 값이 중간에 비어있는 경우 list 보다 dict가 효율적일 수 있습니다.

d = {
    100000000000: 1
}

이런 코드의 경우 배열로 표현하면 매우 많은 원소가 필요하기 때문에 메모리도 많이 먹꼬 성능도 떨어지게 됩니다.


두번째로 반복자의 개념에 대해서 다뤘습니다. 학교 수업에선 배열과 range만 가르치는데, 실제 구현에 관련된 iterator도 이해하고 넘어가야 하는 개념이거든요.

for key in dictionary.keys():
    pass

iterator는 다음 원소를 뱉는 기능이 있는 함수로 생각하시면 됩니다. 인터페이스라고 얘기하긴 했지만, 핵심적인 함수가 한 개뿐인 인터페이스인지라 그냥 함수라고 생각하셔도 됩니다. (실제로 함수 선언만으로 iterator 인터페이스를 구현할 수 있습니다).

이 개념이 왜 중요하냐면,

for key in range(100000000000000):
    pass

위와 같은 코드가 메모리 부족 오류가 뜨지 않고 잘 실행되는 원리를 알아야 하기 때문입니다. 만약 range(100000000000000)이 배열을 반환한다면, 원소의 개수가 100000000000000개인 배열을 할당해야 하는데, 그려면 최소 90 테라 바이트의 메모리가 필요합니다. 그런데 실제로는 그런 일이 일어나지 않죠. 그게 iterator의 핵심입니다.


그 뒤엔 객체 지향의 개념에 대해 얘기했습니다. 인터페이스는 간단히 말해 특정 기능들의 집합입니다.

주의해야 할 점은 파이썬에 인터페이스는 존재하지 않는다는 점입니다. 그럼에도 인터페이스라고 표현한 건 인터페이스를 정의하는 문법이 없을 뿐, 암시적(implicit)인 인터페이스는 많이 쓰이기 때문입니다.

class Car:
    def wheels(self):
        return 4

이런 식으로 클래스를 정의하면

car = Car()
print(car.wheels())

처럼 쓸 수 있습니다. 그런데 생각해보면 모든 차의 바퀴가 4개인 건 아니죠. 이럴 땐 해결할 방법이 여러가지 있습니다.

우선 클래스를 만들 때 인자로 받는 방법이 있습니다.

class Car:
    def __init__(self, wheels):
        self.num = wheels
    def wheels(self):
        return self.num
bike = Car(2)
normalCar = Car(4)

클래스가 변수를 묶어준다는 개념으로 사용하는 경우인데요, dict로도 변수를 묶어줄 수 있지만, 클래스를 사용하면 아래 코드에 비해 큰 장점이 있습니다.

bike = {'wheels': 2}
normalCar = {'wheels': 4}

이런식으로 짜는 것에 비해 오타로 인해 문제가 발생할 가능성이 현저히 줄어든다는 게 그 장점입니다.

오버라이딩은 이번 주 스터디에서 다룰 내용이 아니었기에 넘어갔습니다.

다른 방법은 인터페이스 비슷한 걸 사용하는 방법입니다. 엄밀히 말하면 파이썬엔 인터페이스가 없지만, 암시적(implicit)인 인터페이스는 만들 수 있습니다.

class Bike:
    def wheels(self):
        return 2
class BigTruck:
    def wheels(self):
        return 8

# car = Bike()
# car = BigTruck()
car.wheels()

이 코드에서 주석을 하나씩 해제해보면 인터페이스의 기본 원리를 알 수 있습니다. car이라는 변수가 Bike인지, BigTruck인지 알 필요 없다는 게 인터페이스의 핵심입니다.


마지막으로 질문 있으면 언제든지 톡 주세요. 이 방에 올리셔도 되고 갠톡으로 질문하셔도 됩니다.