<스파르타 웹개발> 3주차 개발일지 (헷갈렸던 python 관련 내용들)
이번주의 주제는 Python과 DB이다.
Python
python은 기존에 사용해본 적이 있고, 기본 문법 자체가 워낙 간단하기 때문에 문법을 정리할 필요는 없을 것 같고,
항상 헷갈리고 벽처럼 다가오던 내용들을 따로 정리해볼까 한다.
그것은 바로..
- map
- (Iterable, Iterator)
- lambda
- *arg, **kwarg
- @ (decorator)
이다.
코딩 초보인 나에게는 뭔가 고급 기능처럼 보이지만, 잘 이해가 안되어서 사용은 못 해봤던 개념들이다.
이번 기회에 차근차근 정리해서 까먹으면 찾아볼 수 있도록 해보자.
1. map
파이썬은 여러가지 내장함수(Built-in functions)들을 가지고 있는데
따로 패키지나 라이브러리를 설치하지 않아도, 파이썬 자체로 사용할 수 있는 함수들을 내장함수라고 한다.
이러한 함수들은 builtins.py이라는 파일과 연결이 되어 있고 이 파일에 들어가 보면 이 함수가 어떤 함수인지 알 수 있다.
builtin.py 파일에 정의되어 있는 map 함수의 설명(주석)은 다음과 같다.
어쩌구 저쩌구 머리가 아프지만 차근 차근 살펴보자.
map(func, *iterables)
즉 map함수는 어떠한 함수(func)과 반복가능한 객체(iterable object, 리스트, 튜플 등) 을 parameter(매개변수)로 받는 함수이다.
주석의 내용을 풀어보자면
iterator가 특정 function을 실행하는데, 이 때 parameter로 iterable에서 하나씩 가져다가 실행한다는 뜻이다.
for문과 비슷한 느낌이다.
이 때 iterator라는 말은 처음 봤기 때문에 또한번 용어 정리를 하고 넘어가자.
Iterable과 Iterator
https://tibetsandfox.tistory.com/27
파이썬(python) - 이터레이터(Iterator)
이터레이터(Iterator)란? 이터레이터는 순서대로 다음 값을 리턴할 수 있는 객체를 의미합니다. 자체적으로 내장하고 있는 next 메소드를 통해 다음 값을 가져올 수 있습니다. 여기서 '순서대로'라
tibetsandfox.tistory.com
위의 페이지를 참고하여 정리하였다.
파이썬에는 여러 자료형이 있는데(int, str, list, 등등..)
그 중 특별한 자료형들이 있다.
- 컬렉션 타입 : 객체가 여러개의 요소를 가지고 있는 경우, list, tuple, set, dictionary가 여기 속한다.
- 시퀸스 타입 : 객체 내부에 순서가 존재하는 경우, list, tuple, range, str
Iterable object란, 내부 요소를 하나씩 순회할 수 있는 (= for 문을 돌릴 수 있는) 객체들이다.
즉 위에 얘기한 collection type, sequence type의 객체들이 iterable object이다.
그럼 그 순회는 누가 할까? for문이 할까?
그 순회를 시행하는 객체가 바로 Iterator이다.
대표적인 iterable인 list를 for문이 순회하는 다음의 과정을 생각해보자.
list_1 = [1, 2, 3]
for i in list_1:
print(i)
위와 같은 코드를 실행시키면
for 문 내부에서는 다음의 과정이 일어난다.
- parameter로 받은 iterable lobject에 iter() 메소드를 이용하여 iterator 객체가 생성된다.
- __next__() 메소드로 iterator가 하나씩 다음 요소로 넘어간다.
즉 자세히 보면 다음과 같은 과정이 일어나는 것과 동일하다.
list_1 = [1, 2, 3]
# iterator 객체 생성 by iter()
iterator = iter(list_1)
# next로 iterator를 넘겨가면서 순환
print(next(iterator))
print(next(iterator))
print(next(iterator))
print(next(iterator))
정리하면..
Iterable 객체는 여러 요소를 가진 객체이고, iter() 메소드를 이용하여 iterator를 생성할 수 있다.
생성된 Iterator는 __next__() 메서드를 통해 iterable의 데이터에 순서대로 접근할 수 있다.
다시 map 함수로 돌아가보자.
map(func, *iterables)
사실 왜 iterable 앞에 *이 붙는지는 아직 모르지만..., 개념을 알고나니 느낌이 온다.
즉 func과 iterable을 매개변수로 받아서
1. iterator를 생성하고 얘가 iterable을 하나씩 순회하면서
2. function을 실행하는 것이다.
for문과 거의 비슷하다. for문을 쉽게 한줄로 쓸 수 있게 해준다고 이해하고 넘어가면 될 것 같다.
map 함수의 사용은 아래와 같다.
예를 들어 [1, 2, 3, 4, 5]라는 list에다가 모든 원소에 1 씩 더해서 새로운 list를 만드는 작업을 한다고 가정해보자.
for문을 사용하면 다음과 같은 코드가 나온다.
mylist = [1, 2, 3, 4, 5]
newlist = []
for i in mylist:
newlist.append(i)
하지만 map 함수를 이용하면 다음과 같이 진행할 수 있다.
mylist = [1, 2, 3, 4, 5]
# 1을 더해주는 함수를 만들고
def add_one(n):
return n+1
# map 함수를 이용하자
# map 함수는 map 객체를 반환하기 때문에 list로 바꿔줘야 한다.
newlist = list(map(add_one, mylist))
1을 더해주는 과정이 간단하여 쉽게 map의 장점이 드러나진 않지만
iterable의 각 원소에 복잡한 function을 적용해야 할 경우에는 매우 편리하게 사용할 수 있을 것 같다.
2. lambda
lambda 또한 map처럼 알고있으면 편리하게 사용할 수 있을 것 같아 보였으나 잘 몰라서 못 쓴 함수 중 하나이다.
map이 반복작업을 쉽게 만들어주는 함수라면,
lambda는 간단한 함수를 따로 이름 없이 쉽게 만들어주는 기능이라고 생각하면 될 것 같다.
lambda 매개변수 : 표현식
으로 사용하게 되며 간단히 다음과 같다.
두 수를 더해주는 함수가 필요한 경우 우리는 보통 다음과 같이 코드를 짠다.
def sum(x, y):
return x + y
sum(10, 20)
람다를 사용하면 다음과 같이 한줄로 끝난다.
(lambda x,y: x + y)(10, 20)
람다 매개변수 : 표현식
또는 변수에 lambda 객체를 할당하여 다음과 같이 표현할 수도 있다.
plus_one = lambda x,y: x + y
plus_one(10, 20)
이렇게 보면 람다가 대체 왜 유용한지 의문이 들 수도 있지만,
위에서 정리한 map이나 filter 와 같이 함수를 매개변수로 받는 메서드와 함께 사용할 때 진가가 드러나는 것 같다.
1) Map
map은 위에서 살펴보았듯 함수 하나, iterable 하나를 매개변수로 받는다.
이때 함수에 람다를 사용할 수 있고, 예시는 다음과 같다.
mylist = [1, 2, 3, 4, 5]
# 1을 더해주는 함수를 따로 만들지 않고 lambda를 이용하여 map 함수를 이용하면?
newlist = list(map(lambda x:x+1, mylist))
2) Filter
Filter 함수는 마찬가지로 function과 iterable을 매개변수로 받아서
iterable의 원소들을 순서대로 function에 넣어서 True일 경우에만 반환한다.
filter와 lambda를 같이 사용하면 다음과 같은 걸 할 수 있겠다.
list(filter(lambda x: x < 5, range(10)))
결과는 [0, 1, 2, 3, 4]가 나온다.
3. *arg, **kwarg
파이썬을 사용하면서 이런 저런 라이브러리나 패키지를 받아서
라이브러리 내의 메서드가 어떤 기능을 하는지 찾아보려고 해당 함수가 정의된 파일에 들어가 보면
이렇게 함수가 매개변수로 *args나 **kwargs를 받는 것을 볼 수 있는데
이게 대체 뭔지 모르겠어서 정확한 해석이 안될 때가 많다.
마찬가지로 차근 차근 정리해보자.
1) *args
매개변수를 출력하는 함수를 만들고 싶다고 해보자.
def out(n):
print(n)
out(1) 의 결과는 당연히 1이 되겠다.
그런데, 이 때 1, 2, 3을 출력하고 싶어서 out(1, 2, 3)을 실행하면 어떻게 될까?
out()함수는 argument 1개를 취하나, 3개가 들어갔다고 TypeError가 뜨게 된다.
즉, 함수를 정의하는 과정에서 매개변수가 몇개일지 확실하지 않은 경우가 있을 수 있는데, 이때 *args를 사용하게 된다.
그렇다면 다음과 같이 함수를 정의하면
def out(*args):
print(args)
tuple 형태로 잘 출력되는 것을 볼 수 있다.
사실
*args라는 명칭을 꼭 쓸 필요는 없고, * 자체가 중요하다.
args는 통상적으로 쓰는 arguments의 약자일 뿐이며,
매개변수에 *을 붙일 경우에는 *(매개변수) 자리에 받는 매개변수가 여러개여도 이를 모두 tuple로 받게 되는 원리이고
함수를 정의함에 있어서 매개변수 여러개 중 특정 원소에 접근할 때에는 indexing을 해주면 된다.
예를 들어,
def get_second(*s):
print(s[1])
get_second(1, 2, 3)
>>> 2
위와 같은 결과를 얻을 수 있다.
2) **kwargs
*args와 비슷하게 굳이 kwargs라는 명칭을 사용할 필요는 없고, **가 붙으면 된다.
kwargs는 keyword arguments의 약자이며
args가 tuple 형태로 여러 매개변수를 저장하는데 비해
kwargs는 dictionary 형태로 매개변수를 저장하는 것이 차이점이다.
파라미티 명 = 파라미터 값 과 같은 형태로 파라미터를 주면, 이를 kwargs로 인식하고 딕셔너리에 저장하게 된다.
def characteristics(**haha):
print(haha)
characteristics(키=175, 나이=30)
>>> {'키': 175, '나이': 30}
이런 식으로 말이다.
4. @ (decorator)
참고자료
https://dojang.io/mod/page/view.php?id=2427
파이썬 코딩 도장: 42.1 데코레이터 만들기
Unit 42. 데코레이터 사용하기 파이썬은 데코레이터(decorator)라는 기능을 제공합니다. 데코레이터는 장식하다, 꾸미다라는 뜻의 decorate에 er(or)을 붙인 말인데 장식하는 도구 정도로 설명할 수 있습
dojang.io
함수의 시작과 끝에 특정 문구를 출력하고 싶은 상황이라고 하자.
가장 간단하게는 다음과 같이 코딩할 수 있다.
def hello():
print('hello 함수 시작')
print('hello')
print('hello 함수 끝')
def world():
print('world 함수 시작')
print('world')
print('world 함수 끝')
hello()
>>> hello 함수 시작
hello
hello 함수 끝
world()
>>> world 함수 시작
world
world 함수 끝
그런데 여러 함수에 모두 이런 기능을 넣고 싶다면?
너무나도 귀찮다.
이럴 때 사용하는 것이 decorator이다. (함수를 꾸며준다고 해서 이런 이름이 붙었다고 한다.)
다음과 같이 alarm이라는 함수를 정의하자.
def alarm(func): # 호출할 함수를 매개변수로 받음
def wrapper(): # 호출할 함수를 감싸는 함수
print(func.__name__, '함수 시작') # __name__으로 함수 이름 출력
func() # 매개변수로 받은 함수를 호출
print(func.__name__, '함수 끝')
return wrapper # wrapper 함수 반환
def hello():
print('hello')
trace_hello = trace(hello) # 데코레이터에 호출할 함수를 넣음
trace_hello() # 반환된 함수를 호출
>>> hello 함수 시작
hello
hello 함수 끝
alram은 특정 함수를 매개변수로 받고
함수 내에 wrapper 함수가 다시 있다.
trace_hello라는 객체에 trace(hello)를 실행해주면, 여기에 wrapper가 리턴된다.
이를 실행해주면 우리가 원하는 결과를 얻을 수 있다.
왜 wrapper가 굳이 필요할까? : 는 잘 모르겠지만.. 일단 데코레이터를 편하게 사용하기 위한(@) 약속이라고 생각하고 넘어가자.
이러한 과정을 더 쉽게 만들어주는 것이 @ 데코레이터이고
def trace(func): # 호출할 함수를 매개변수로 받음
def wrapper():
print(func.__name__, '함수 시작') # __name__으로 함수 이름 출력
func() # 매개변수로 받은 함수를 호출
print(func.__name__, '함수 끝')
return wrapper # wrapper 함수 반환
이러한 함수를 만든 후,
특정 함수를 정의할 때 함수 위에 @trace 데코레이터를 붙여주고 정의해주면
@trace # @데코레이터
def hello():
print('hello')
hello()
위와 같은 결과를 얻을 수 있다.
일단 여기까지만 알고 넘어가자.
Database
DB를 사용하는 이유는 '잘 저장하기' 위해서가 아니라 '잘 꺼내보기' 위해서이다.
DB의 종류에는
- SQL (Structured Query Language)
- No-SQL (Not noly SQL or Non SQL)
두가지가 있으며, 각 형식을 제공하는 DB들은 매우 다양한데
SQL에는 내가 잠깐 맛만 보았던(엑셀 수준으로도 사용 못했던..) MySQL이나 MariaDB 등이 있고
No-SQL에는 대표적으로 본 강의에서 사용하는 Mongo DB가 있다.
SQL은 쉽게 말해 엑셀(표) 형태이다. (정형화된 데이터)구조가 간단한 장점이 있지만, 새로운 컬럼을 추가하게 되면 기존 데이터들의 처리에 문제가 생기는 단점이 있다.
(ex. 회원들의 성별, 나이, 이름만 10년 모았는데 갑자기 주소도 모으고 싶다. 이미 사용하던 표에 컬럼 하나를 추가해야 하는데 어떻게 하지..?)
No-SQL은 key-value, document, graph 등 여러 형태를 갖는데 (비정형 데이터)새로운 데이터를 추가하기에 편리하여 startup 등에서 많이 사용한다고 한다.
이번 강의에서는 python을 이용하며, DB를 조작하기 위해서 pymongo라는 패키지를 사용하게 된다.
pymongo의 기초적인 사용법(CURD)는 다음과 같다.
from pymongo import MongoClient
client = MongoClient('mongodb+srv://test:sparta@cluster0.sumbhxu.mongodb.net/?retryWrites=true&w=majority')
db = client.dbsparta
# 저장 - 예시
doc = {'name':'bobby','age':21}
db.users.insert_one(doc)
# 한 개 찾기 - 예시
user = db.users.find_one({'name':'bobby'})
# 여러개 찾기 - 예시 ( _id 값은 제외하고 출력)
all_users = list(db.users.find({},{'_id':False}))
# 바꾸기 - 예시
db.users.update_one({'name':'bobby'},{'$set':{'age':19}})
# 지우기 - 예시
db.users.delete_one({'name':'bobby'})
패키지 사용법은 뭐 그때그때 찾아보면 되고, 위의 DB의 개념에 대해서만 잘 기억하고 넘어가면 될 것 같다.
'코딩 > 스파르타코딩클럽' 카테고리의 다른 글
<스파르타 App개발> 1주차 개발일지 (0) | 2022.07.25 |
---|---|
<스파르타 웹개발> 5주차 개발일지 (完) (0) | 2022.07.08 |
<스파르타 웹개발> 4주차 개발일지 (0) | 2022.07.02 |
<스파르타 웹개발> 2주차 개발일지 (0) | 2022.06.23 |
<스파르타 웹개발> 1주차 개발일지 (0) | 2022.06.17 |