인코딩하기
a = "Life is too short"
b = a.encode('utf-8')
print(type(b))
클래스가 바이트로 변환된걸 확인 가능
a = "한글"
a.encode("ascii")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
아스키는 한글을 표현할수 없어서 에러가 남
a = "한글"
a.encode("euc-kr")
이렇게 euc-kr로 바꾸면 바이트로 잘 인코딩이 된다.
디코딩하기
인코딩한 바이트 문자열을 유니코드 문자열로 되돌린다
# euc_kr.py
# 1. euc-kr로 작성된 파일 읽기
with open('euc_kr.txt', encoding='euc-kr') as f:
data = f.read() # 유니코드 문자열
# 2. unicode 문자열로 프로그램 수행하기
data = data + "\n" + "추가 문자열"
# 3. euc-kr로 수정된 문자열 저장하기
with open('euc_kr.txt', encoding='euc-kr', mode='w') as f:
f.write(data)
파일을 오픈할때나 저장할때나 인코딩 방시은 맞춰 줘야 한다
인코딩 방식 생략하면 기본값으로 utf-8로 지정한다.
소스 코드의 인코딩
파이썬은 소스 코드의 인코딩을 명시하고자 소스 코드 가장 위에 다음과 같은 문장을 넣어야 한다.
# -*- coding: utf-8 -*-
# -*- coding: euc-kr -*-
07-2 클로저와 데코레이터
클로저
함수안의 한수라는 뜻
# closure.py
class Mul:
def __init__(self, m):
self.m = m
def mul(self, n):
return self.m * n
if __name__ == "__main__":
mul3 = Mul(3)
mul5 = Mul(5)
print(mul3.mul(10)) # 30 출력
print(mul5.mul(10)) # 50 출력
함수를 매번 만들어주기 보다 이렇게 클래스로 지정해주면 간편하게
찍어낼수가 있다.
# closure.py
class Mul:
def __init__(self, m):
self.m = m
def __call__(self, n):
return self.m * n
if __name__ == "__main__":
mul3 = Mul(3)
mul5 = Mul(5)
print(mul3(10)) # 30 출력
print(mul5(10)) # 50 출력
이렇게 하면 입력된것이 바로 콜이 실행되게 함
함수안에 함수가 있고 그 함수를 리턴해주는 것을 클로저라고 부른다
데코레이터란?
import time
def myfunc():
start = time.time()
print("함수가 실행됩니다.")
end = time.time()
print("함수 수행시간: %f 초" % (end-start))
myfunc()
스타트 엔드를 모든 함수마다 넣어주면 너무 비효율적이라서 클로저를 쓴다
# decorator.py
import time
def elapsed(original_func): # 기존 함수를 인수로 받는다.
def wrapper():
start = time.time()
result = original_func() # 기존 함수를 수행한다.
end = time.time()
print("함수 수행시간: %f 초" % (end - start)) # 기존 함수의 수행시간을 출력한다.
return result # 기존 함수의 수행 결과를 반환한다.
return wrapper
def myfunc():
print("함수가 실행됩니다.")
decorated_myfunc = elapsed(myfunc)
decorated_myfunc()
이 래퍼안에 지금 이 함수(myfunc) 대신에 우리가
수행하고자 하는 함수가 들어간 거기 때문에 수행시간이 자동으로 측정된다
이렇게 기존 함수를 바꾸지 않고 기능을 추가할 수 있게 만드는 elapsed 함수와 같은 클로저를 데코레이터(decorator)라고 한다.
# decorator.py
import time
def elapsed(original_func): # 기존 함수를 인수로 받는다.
def wrapper():
start = time.time()
result = original_func() # 기존 함수를 수행한다.
end = time.time()
print("함수 수행시간: %f 초" % (end - start)) # 기존 함수의 수행시간을 출력한다.
return result # 기존 함수의 수행 결과를 반환한다.
return wrapper
@elapsed
def myfunc():
print("함수가 실행됩니다.")
# decorated_myfunc = elapsed(myfunc) # @elapsed
데코레이터로 인해 더이상 필요하지 않다.
# decorated_myfunc()
myfunc()
@elapsed(@+함수명)
이라고 하기만 해도 내가 데코레이터를 입히고자 하는 함수위에다가 입력해주면
데코레이터 함수가 적용이 된다
이 템플릿이 적용이 된다
이대로 마이펑션을 실행하게 되면 아까처럼 함수 실행 시간이
내가정의한 함수가 별도로 변경 안해도 이 데코레이터에 들어가서
래퍼안에 이렇게 함수로 받아져서 템플릿이 적용된 것을 확인할수 있다.
# decorator2.py
... 생략 ...
@elapsed
def myfunc(msg):
print("'%s'을 출력합니다." % msg)
myfunc("You need python") # 출력할 메시지를 myfunc 파라미터로 전달한다.
이렇게 데코레이터에 파라미터를 준다면
에러가 난다.
Original_func에서 파라미터를 안받기 때문에 그 인수 개수가 안맞아서 그렇다
에러가 발생
래퍼 함수를 수정하면 된다
# decorator.py
import time
def elapsed(original_func): # 기존 함수를 인수로 받는다.
def wrapper(*args, **kwargs):
start = time.time()
result = original_func(*args, **kwargs)) # 기존 함수를 수행한다.
end = time.time()
print("함수 수행시간: %f 초" % (end - start)) # 기존 함수의 수행시간을 출력한다.
return result # 기존 함수의 수행 결과를 반환한다.
return wrapper
@elapsed
def myfunc(msg):
print("함수가 실행됩니다.")
# decorated_myfunc = elapsed(myfunc) # @elapsed 데코레이터로 인해 더이상 필요하지 않다.
# decorated_myfunc()
myfunc()
*args, **kwargs
이렇게 인수 개수가 여러 개가 들어와도 받겠다 이런뜻임
**kwargs이것도 키 밸류 형태로 들어오는것도 딕셔너리 형태로 다 받겠다는 뜻
어떤 형태로 들어올지 모르니 다 받겠다 이런 뜻
07-3 이터레이터와 제너레이터
이터레이터란?
이터레이터 반복 가능한 객체라는 뜻
이터레이블하다 해서 이터레이터는 아니다.
넥스트 함수를 쓸수 있어야 이터레이터라고 한다
>>> a = [1, 2, 3]
>>> ia = iter(a)
>>> type(ia)
<class 'list_iterator'>
이렇게 하나씩 빼서 넥스트를 할 때마다 다음께 출력이된다.
a = [1, 2, 3]
ia = iter(a)
print(type(ia))
print(next(ia))
print(next(ia))
print(next(ia))
네개를 하게되면 에러가 뜬다 스톱이터레이션 에러가 뜬다
이런 에러를 방지하기 위해 for문 쓴다
>>> a = [1, 2, 3]
>>> ia = iter(a)
>>> for i in ia:
... print(i)
...
1
2
3
>>> a = [1, 2, 3]
>>> ia = iter(a)
>>> for i in ia:
... print(i)
...
1
2
3
>>> for i in ia:
... print(i)
...
>>>
이터레이터 특징상 for문 쓰고 한 번 더 이렇게 실행을 해주게 되면
처음에 여기서 123 하고 그다음에 출력이 안된다
다음for문엔 다음 이터레이터 나올게 없기에 안 나온다
. 즉, for문이나 next로 그 값을 한 번 읽으면 그 값을 다시는 읽을 수 없다는 특징이 있다.
이터레이터 만들기
# iterator.py
class MyIterator:
def __init__(self, data):
self.data = data
self.position = 0
def __iter__(self):
return self
def __next__(self):
if self.position >= len(self.data):
raise StopIteration
result = self.data[self.position]
self.position += 1
return result
if __name__ == "__main__":
i = MyIterator([1,2,3])
for item in i:
print(item)
생성자에서 이터레이블한 리스트 같은데이터를 받아서 넣는다
포지션은 0
or 문, iter() 함수, next() 함수 등에서 사용하기 위해 필수이다
인덱스가 총 개수를 넘어서게 되면 스톱 이터레이션 예외처리를 발생하고 오류 띄운다
1
2
3
이거를 거꾸로 출력하자면
# reviterator.py
class ReverseIterator:
def __init__(self, data):
self.data = data
self.position = len(self.data) -1
def __iter__(self):
return self
def __next__(self):
if self.position < 0:
raise StopIteration
result = self.data[self.position]
self.position -= 1
return result
if __name__ == "__main__":
i = ReverseIterator([1,2,3])
for item in i:
print(item)
여기서는 포지션을 len - 1 즉 마지막 인덱스다
그리고 넥스트 함수에서는 -1씩 감소 시키는 그런 형태
제너레이터란?
Next 함수 호출 시 그 값을 차례대로 얻을 수 있다.
def mygen():
yield 'a'
yield 'b'
yield 'c'
g = mygen()
print(type(g))
print(next(g))
print(next(g))
print(next(g))
타입은 g가 뜨고 마찬가지로 넥스트 출력하면 abc차례대로 출력
4개출력시 StopIteration 에러
제너레이터 표현식
# generator.py
def mygen():
for i in range(1, 1000):
result = i * i
yield result
gen = mygen()
print(next(gen))
print(next(gen))
print(next(gen))
제곱식 출력하는 이터레이터를 좀더 간편하게 하려면?
gen = (i * i for i in range(1, 1000))
print(next(gen))
print(next(gen))
print(next(gen))
리스트 대신에 대괄호를 소괄호로 바꾸면 제너레이터가 된다
1부터 1000미만의 i를 가져와서 i제곱을 해준거를
그대로 제너레이터를 바로 만들수가 있다.
제너레이터와 이터레이터
class MyIterator:
def __init__(self):
self.data = 1
def __iter__(self):
return self
def __next__(self):
result = self.data * self.data
self.data += 1
if self.data >= 1000:
raise StopIteration
return result
(i * i for i in range(1, 1000))를 클래스로 만든 예시다.
이렇게 간단한 경우라면 이터레이터 클래스보다는 제너레이터 표현식을 사용하는 것이 훨씬 간편하고 이해하기 쉽다.
제너레이터 활용하기
# generator2.py
import time
def longtime_job():
print("job start")
time.sleep(1) # 1초 지연 - 실제로는 데이터베이스 조회, 파일 처리 등을 시뮬레이션
return "done"
list_job = [longtime_job() for i in range(5)]
print(list_job[0]) # 첫 번째 결과만 필요한 상황
다섯개를 모두 정의한 상태로 0번 인덱스를 가져와서 실행했기 때문에
이제 리턴이 던이라서 이렇게 한번 리턴이 나옴
이렇게 리턴이 나오는데 하나만 필요한데 나머지를 다 실행을
해놓고 불러와서 기다릴 필요는 없다
이럴때 제너레이터를 사용한다
[]된거를 소괄호로 바꿔서 요렇게만들게 되면 제너레이터라고
한다.
# generator2.py
import time
def longtime_job():
print("job start")
time.sleep(1)
return "done"
# 제너레이터 표현식: 함수를 미리 실행하지 않고 필요할 때만 실행
list_job = (longtime_job() for i in range(5))
print(next(list_job)) # 첫 번째 값만 요청
이터레이터와 제너레이터 차이
이터레이터는 데이터를 메모리에 쫙다 올려놓고 실행
제너레이터는 필요할때마다 하나씩 생성
메모리 사용이 효율적이다.
넥스트를 이용해서 첫번째 실행을 했을 때
잡스타트를 한번만 나오고 던이 나온다
다섯번을 미리 다 메모리에 올려 놓고 하는게 아니라
넥스트를 실행될 때마다 그때 그때 실행됨
이런 방식을 '느긋한 계산법(lazy evaluation)'이라고 한다.
07-4 파이썬 타입 어노테이션
동적 언어와 정적 언어
자바는 정적언어 파이썬은 동적언어다
>>> a = 1
>>> type(a)
<class 'int'>
>>> a = "1"
>>> type(a)
<class 'str'>
파이썬은프로그래밍 실행중에 변수타입을
바꿀수 있다.
int a = 1; // a 변수를 int형으로 지정
a = "1"; // a 변수에 문자열을 대입할 수 없으므로 컴파일 에러
자바는 한번 지정해놓으면 못 바꾼다.
동적언어 장점 유연한 코딩, 깔끔한 소스코드
안정성을 선호하는 금융권에선 정적언어 선택하는 경향 있다.
파이썬 타입 어노테이션
num: int = 1
def add(a: int, b: int) -> int:
return a + b
이렇게 하면 파라미터 타입, 리턴타입까지 ->로 정할수 있다.
이렇게 어노테이션을 주면 힌트를 알려 주는 정도의 기능만 지원
mypy
파이썬 자체로는 타입 어노테이션을 검사하지 않지만, 별도 도구를 사용하면 더 적극적으로 타입을 검사할 수 있다. 그 대표적인 도구가 바로 mypy이다.
표준라이브러리 아니므로 설치 필요
c:\doit> pip install mypy
설치 후 python -m mypy 7-4.py 명령어를 치면
Success: no issues found in 1 source file
이슈가 없다면 이런 메시지가 뜸
이슈가 있다면 이런 메시지
7-4.py:6: error: Argument 1 to "add" has incompatible type "str"; expected "int" [arg-type]
7-4.py:6: error: Argument 2 to "add" has incompatible type "str"; expected "int" [arg-type]