[Python] 파이썬 날아오르기

shlim55·2025년 11월 9일

Python

목록 보기
19/25

인코딩하기

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)
  1. init 메서드: 이터레이터를 초기화한다

생성자에서 이터레이블한 리스트 같은데이터를 받아서 넣는다
포지션은 0

  1. iter 메서드: 이터레이터 객체 자신을 반환한다

or 문, iter() 함수, next() 함수 등에서 사용하기 위해 필수이다

  1. 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"

리스트 컴프리헨션: 5번의 작업을 모두 실행해서 리스트로 만든다

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]

profile
A Normal Programmer

0개의 댓글