generator, iterator


  • generator 함수에서는 return 대신 yield를 통해, iterator(반복자)를 차례대로 반환할 수 있다.
    • 함수를 호출했을 때 반환되는 것은 generator 객체이다.
      next()로 generator 함수 내부의 코드를 실행하며,
      yield에 따라 iterator를 차례대로 불러온다.
      • 이를 통해 함수의 코드를 조금씩 실행하여, 메모리 효율성을 꾀할 수 있다.
    • 최초에는 함수를 호출하여 generator 객체를 변수에 저장하고,
      그 이후에는 next()에 해당 변수를 전달하여 iterator를 반환하는 방식으로 사용한다.
    • generator은 coroutine(코루틴)에 응용된다.
  • iter()에 iterable 자료형을 인자로 두어 호출하면 iterator 객체로 만들며,
    마찬가지로 next()로 iterator를 차례대로 하나씩 가져올 수 있다.
  • generator 객체와 iterator 객체에서 iterator를 모두 반환했을 시,
    StopIteration 예외를 발생시키거나 아무것도 반환하지 않는다.

decorator


  • (함수) decorator는 함수에 사용되며,
    대상 함수의 앞 뒤를 꾸미거나 반복할 내용을 decorator에서 정의하는 것이다.
    • 그러한 내용이 필요한 함수에서는 decorator를 사용하여
      간편하게 함수를 정의할 수 있게 된다.
  • wrapper()에 functools 라이브러리의 @wraps를 사용하면,
    쓰지 않았을 때 발생하는 debugging 관련 문제를 보완할 수 있다.
    • @wraps에는 인자로 decorator 함수의 매개변수인 함수 객체를 전달한다.
    • decorator를 정의할 때 반드시 함께 사용하는 편이 좋다.

usage

from functools import wraps

def test(function):
    @wraps(function)
    def wrapper(*args, **kwargs):
        print("안녕하세요.")
        function(*args, **kwargs)
        print("안녕히 가세요.")
    return wrapper
- 여기서, test가 decorator명이다.
- test()의 매개변수로는 decorator를 사용하는 함수 객체명이 전달되게 된다.
- test()에서는 wrapper()를 정의하며, wrapper 객체명을 return한다.
  - wrapper는 decorator를 사용하는 함수의 앞 뒤를 꾸며주는 내용을 작성하는 부분으로,
    test()의 매개변수인 function이 wrapper에서 쓰인다.
    - function()이 있는 위치에서 decorator를 사용하는 함수가 실행된다. 
    - decorator를 사용하는 함수에서 인자를 어떻게 전달할 지 정해지지 않았다면
      이에 상관없이 동작할 수 있도록 *args, **kwargs을 매개변수로 작성한다. 


@test
def hello():
    print("반갑습니다.")
- decorator를 사용하는 함수의 정의부 상단에는 '@decorator명'을 작성한다.


hello()
# 안녕하세요.
# 반갑습니다.  
# 안녕히 가세요. 

with statement


  • with문에는 context manager인 클래스의 인스턴스를 주로 사용한다.
    • context manager인 클래스에서는 두 매직 메서드를 필수적으로 정의한다.
      각 메서드는 with문에 진입한 객체에 대하여 호출된다.
      • __enter__: with문에 진입 시 자동 호출
      • __exit__: with문을 벗어날 때 자동 호출
  • 주로, 리소스를 사용하고 반드시 반환해야 할 필요가 있을 때 사용한다.
    • 이에 따라 주로 파일 객체를 다룰 때 쓰곤 하는데,
      open()으로 생성되는 파일 객체도 context manger인 것이다.
      • with문 진입 시에 __enter__에 의해 파일 객체가 return되고,
        with문을 벗어날 때 __exit__에 의해 파일 객체.close()가 호출되는 것이다.
  • with문은 중첩이 가능하며, 한 번에 여러 객체를 사용할 수도 있다.

usage

# _context_manger.py 
class _ContextManager:

    def __init__(self, number):
        self.number = number
        print(f'{self.number} created')
- 여기서 __init__을 정의했다 하더라도 __enter__에서 self를 return하지 않았다면, 
  with문 내에서 as 변수로 객체를 활용할 수는 없다. (None이 된다.)

    def __enter__(self):
        print(f'{self.number} enter')
        return self
- __enter__에서 self(객체 자기 자신)return 해줌으로써,
  with문 내에서 as 변수로 객체를 활용할 수 있다.
  -, __enter__의 return 값이 as 변수에 저장된다는 말이다. 

    def __exit__(self, _type, _value, _traceback):
        print(f'{self.number} exit')
- 세 매개변수들은 with문 내에서 발생한 예외에 대한 정보를 나타낼 때 사용하며, 필수적으로 요구된다.

    def foo(self):
        print(f'{self.number} foo')

cm_1 = _ContextManager(1)

with _ContextManager(2) as cm_2:
    cm_1.foo()
    cm_2.foo()
    print(cm_2)
    print('bar')


# output
1 created
2 created
2 enter
1 foo
2 foo
<__main__._ContextManager object at 0x00000157014F4F70>
bar
2 exit
- 만약 __enter__()에서 return self가 없었다면 cm_2는 None으로 취급되어,
  cm_2.foo()에서 에러가 발생했을 것이다. 다만, __exit__()는 호출된다.

global


  • 가끔 UnboundLocalError를 마주칠 때가 있는데, 함수 scope 밖에서 생성된 variable을
    해당 함수 내에서 직접적으로 재할당하려고 하는 코드가 있기 때문에 에러가 발생한다.
    (이 밖엔 로직의 문제로, 변수에 값이 할당되기 전에 참조하려고 해서 발생하기도 한다.)
    • 해당 함수에서 단순히 참조만 한다면, 이는 괜찮다.
  • 매개변수로 받아와서 지역 변수로 할당하여 초기화하던지
    위와 비슷한 말이지만, 함수 내에서 해당 변수를 직접 초기화하던지
    혹은 함수 내에서 global 변수명 으로 전역 변수로 선언해주면 된다.

f-string formatting


  • 기본적으로 f'{variable}'로, 문자열 내에서 변수를 참조할 수 있다.
  • 다양한 형식의 문자열 포매팅도 지원한다.

string

chars = 'hi'

f'{chars:10}'   # 'hi        ': 길이 조절 (10칸)

f'{chars:<10}'  # 'hi        ': 왼쪽 정렬 (기본값)
f'{chars:>10}'  # '        hi': 오른쪽 정렬
f'{chars:^10}'  # '    hi    ': 가운데 정렬

f'{chars:?^10}' # '????hi????': 특정 문자로 공백 채우기

integer

  • {:d}로 포맷팅
  • string에서 다룬 정렬도 사용 가능하다.
x, y = 100, -100

f'{x:5d}'   # '  100': 길이 조절 (5칸)

f'{x:05d}'  # '00100': 0으로 공백 채우기
f'{y:05d}'  # '-0100': 음수 

f'{x:+d}'   # '+100' : '+' 부호 표시
f'{x: d}'   # ' 100' : 부호 생략 

f'{x:+5d}'  # ' +100': 부호 뒤로 밀기
f'{y:=+5d}' # '- 100': 부호 앞으로 밀기

f'{y:+05d}' # '-0100': 부호 표시 및 0으로 공백 채우기

float

  • {:f}로 포맷팅
  • {:g}는 의미없는 소수점을 제거할 수 있다.
z = 18.25

f'{z:f}'     # '18.250000'       : 소수점은 기본적으로 6자리 표시
f'{z:10f}'   # ' 18.250000'      : 길이 조절 (10칸)
f'{z:15f}'   # '      18.250000' : 길이 조절 (15칸)

f'{z:+15f}'  # '     +18.250000' : '+' 부호 표시
f'{z:+015f}' # '+0000018.250000' : 부호 표시 및 0으로 공백 채우기

f'{z:15.3f}' # '         18.250' : 소수점 표시 길이 조절 (3칸)
f'{z:15.2f}' # '          18.25' : 소수점 표시 길이 조절 (2칸)
f'{z:15.1f}' # '           18.2' : 5를 반올림 시, 앞자리가 짝수면 내림

f'{10.0:g}'  # '10'              : 의미없는 소수점 제거

package


  • Python에서는 from과 import를 통해 모듈 및 함수, 변수 등을 불러올 수 있다.
  • 다른 모듈을 참조하기 위한 방법은 크게 2가지로 분류한다.
    • 절대 경로
      • 해당 프로젝트의 루트 디렉터리를 기준으로 참조하는 방법이다.
        (정상적으로 동작하기 위해서, 터미널 등에서 Python 모듈을 실행했을 때
        sys.path에 해당 프로젝트의 루트 디렉터리 경로가 담겨있어야 한다.)
    • 상대 경로
      • 해당 모듈의 현재 위치를 기준으로 참조하는 방법이다.
      • '.'은 현재 디렉터리, '..'은 부모 디렉터리, '...'부터는 조상 디렉터리이다.
    • 디렉터리 간의 계층은 '.'을 통해 구분한다.

__init__.py

  • (다른 모듈로부터) 이 파일이 직속된 디렉터리가 불러와 진 것 만으로도
    반드시 실행되는 특성을 가지고 있다.
    • 이러한 특성으로 인해, 해당 디렉터리의 초기화 용도로 사용하곤 한다.
    • 이 파일에서 선언한 변수는, from 해당 디렉터리 까지 하는 것 만으로도
      변수를 import하여 사용할 수 있다.
      (굳이 이 파일을 from이나 import를 통해 불러올 필요가 없다.)
  • __all__ 변수에 해당 디렉터리의 모듈명들을 원소로 한 리스트를 할당해 놓으면,
    from 해당 디렉터리 import * 을 했을 때 해당 모듈들은 모두 import 된다.

0개의 댓글