Bill Lubanovic의 저서 [Introducing Python]를 요약한다. 이 포스트에서는 이와 같은 것들을 다룬다.: list, tuple, dictionary and set. Also covers code structure and module, package.


Index

  1. 파이 맛보기
  2. 파이 재료: 숫자, 문자열 변수
  3. 파이 채우기: 리스트, 튜플, 딕셔너리, 셋
  4. 파이 크러스트: 코드 구조
  5. 파이 포장하기: 모듈, 패키지, 프로그램

파이 맛보기

파이썬은 인터프리터 언어다. 파이썬이 개발을 빠르게 할 수 있다는 이유도 거대한 크기의 기계어를 만들어내지 않고 실행이 가능하기 때문으로 추정된다.

인터프리터 언어

  • 대표적인 인터프리터 언어는 파이썬, 자바 스크립트가 있다.
  • 인터프리터는 소스코드를 바로 실행한다.
  • 인터프리터는 바로바로 실행을 하다보니 처음에 속도가 비교적 빠르다.

컴파일 언어

  • 대표적인 컴파일 언어는 자바다.
  • 컴파일 언어는 처음 프로그램을 시작할 때 모든 코드를 기계어로 바꾼다 (컴파일 한다).
  • 컴파일을 하기 때문에 처음에 프로그램을 실행할 때 시간이 오래 걸린다.
  • 하지만 한 번 컴파일을 하면 실행시 기계어를 불러와 더 빨리 실행을 할 수 있어 매번 실행시마다 번역을 거치는 인터프리터 언어보다 속도가 더 빨라진다.

파이 재료: 숫자, 문자열 변수

파이썬은 문자를 변형 할 수 없다. st[0] = ’e’ 이런식으로 변경을 할 수 없다. 즉, 불변객체이다.

문자열도 중간에 수정을 할 수 없고 아예 객체를 새로 만드는 replace() 메서드를 이용해서 문자열을 변경해야 한다. 문자열을 불변으로 만드는 이유는 Java와 마찬가지로 프로그래밍을 편리하게 하기 위해서다.


슬라이싱을 이용한 문자 reverse 예시

1
2
st = "eererewrvfgrhfos"
re_st = st[::-1] # sofhrgfvrwereree

파이 채우기: 리스트, 튜플, 딕셔너리, 셋

파이썬에서 기본으로 제공하는 자료구조에 대해 알아본다.

del

자료구조에서 항목을 삭제하는 커맨드는 del 인데, 이 del은 자료구조의 함수가 아닌 파이썬 구문이다. del은 객체로부터 이름을 분리하고 객체의 메모리를 비워준다.


del 예시

1
2
3
del full[2]

# full.del(2) 처럼 쓰지 못한다.

리스트

리스트는 변경 가능하다. 항목을 할당하고, 자유롭게 수정 삭제를 할 수 있다.


리스트 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 생성 
empty = []
empyt2 = list()
full = [1, 2, 3]

# 리스트의 끝에 항목 추가하기  append()
full.appned(4) 

# 리스트 병합하기 extend()
added = [5, 6, 7, 8]
full.extend(added)  # [1, 2, 3, 4, 5, 6, 7, 8]
full += added # [1, 2, 3, 4, 5, 6, 7, 8]

# append 를 쓰면 리스트 자체가 추가된다 
full.appned(added) # [1, 2, 3, 4, [5, 6, 7, 8]]

# 리스트 정렬 
# sort()는 리스트 자체를 내부적으로 정렬한다. 
# sorted() 는 리스트의 정렬된 복사본을 반환한다. 
full.sort()
new_sort = full.sorted() 

튜플

튜플은 불변한다. 튜플에 항목을 할당하고 나서는 바꿀 수 없다. 때문에 튜플을 상수 리스트라 볼 수 있다.


튜플 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 생성 
empty_tuple = ()
# 콤마로 값을 나열해도 튜플을 만들 수 있다. 
empty_tuple = 1, 2, 3
empty_tuple = (1, 2, 3) 

# 튜플의 나열하는 특성을 이용해서 객체 생성없이 swap하기 
password = '12'
icecream = 'sweet' 
password, icecream = icecream, password 

리스트가 아닌 불변객체라 함수 지원이 더 적은 튜플을 사용하는 이유

  • 더 적은 공간을 사용한다.
  • 실수로 값을 바꿀 위험이 없다.
  • 튜플을 딕셔너리 키로 사용이 가능하다.
  • 네임드 튜플 은 객체의 대안이 될 수 있다.
  • 함수의 인자들은 튜플로 전달된다.

딕셔너리


딕셔너리 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 생성 
empty_dict = {}

# dict() 를 이용해 두 값 쌍으로 이뤄진 자료구조를 딕셔너리로 변환할 수 있다. 
lol = [['a', 'b'], ['c', 'd']] 
lol2 = lol #{'a': 'b', 'c': 'd'}

lol3 = ('ab', 'cb') 
lol4 = lol #{'a': 'b', 'c': 'd'}

# 딕셔너리 결합하기 update() 
em = {'a': 'b'}
em2 = {'c': 'd'}

em.update(em2) # {'a': 'b', 'c': 'd'}

# 딕셔너리 비우기 
em.clear() 

# 딕셔너리에 특정 키가 들어있나 확인 
'c' in em 

# 모든 키 가져오기 
em.keys() 

# 모든 값 가져오기 
em.values() 

# 모든 키, 값 가져오기
# ('a', 'b'), ('c', 'd') 처럼 튜플로 반환한다 
em.items()

어떤 것이 존재하는지 여부만 판단하기 위해서 셋을 사용한다. 중복을 허용하지 않는다. 셋은 수학시간에 배웠던 집합과 아주 유사하다.


셋 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 생성 
# 그냥 {} 는 딕셔너리 생성자에 선점되었다. 
empty_set = set() 
empty_set2 = {1, 2, 3, 4}

# 각종 집합

# 교집합 &, intersection()
if contents & {'ice', 'cream'} # ice 와 cream 모두 들어있어야 참 

a = {1, 2} 
b = {3, 4} 

a & b = 2

# 합집합 |, union()
a | b = {1, 2, 3}

# 차집합 -, difference() 
a - b = {1}

# 대칭 차집합 ^, symmetric_difference  
# 각 항목에 별개로 가지고 있는 값을 구한다. 
a ^ b = {1, 3}

# 부분집합 <=, issubset() 
a.issubset(b) # False 
a.issubset(a) # True 
a.issubset((1, 2, 3))

# 슈퍼셋 >=, issuperset() 
a.issuperset((1)) # True
((1, 2, 3)).issuperset(a) # True
a.issuperset(a) # True 

파이 크러스트: 코드 구조

컴프리헨션

내가 가끔 검색해보고는 하는 한 줄 로 for 문 돌리기와 유사하다.

하나 이상의 이터레이터로부터 파이썬 자료구조를 만드는 방법이다. 더 파이써닉한 용법이라니는데 간단한 할당문 말고는 컴프리헨션을 사용하면 더 헷갈릴 것 같다.

한 줄 for 문 예시

1
num = [i for i in range(1, 6)]

인자

다른 언어들과 마찬가지로, 값을 순서대로 상응하는 매개변수에 복사하는 것이 위치인자이다. 키워드인자는 위치인자의 혼동을 피하기 위해 상응하는 이름을 인자 안에 지정한 것이다.


인자 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 위치 인자 
def menu(wine, entree, dessert): 
	pass 

# 키워드 인자 
def menu(wine=wine, entree=entreee, dessert=dessert): 
	pass 

# 인자의 기본 값 지정 
def menu(wine, entree, dessert='pie'): 
	pass 

인자 모으기 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 위치 인자 모으기 * 
def print_args(one, two, three, *args): 
	pass 

# 실제로 호출 시 three까지 위치에 따라 값이 들어가고 나머지는  *args가 인자를 취하게 해준다.
print_args(1, 2, 3, 4, 5, 6, 7, 8) 

# 키워드 인자 모으기 ** 
def print_keyword(**kwargs)

# 실제 호출 시 위치인자와 마찬가지로, 함수에 따로 정의가 안 된 위치인자를 취한다. 
print_keyword(one=1, two=2, three=3, four=4, five=5)

여러가지 종류의 인자들을 섞어서 사용하려면 함수를 정의할 때 위치인자, 키워드 인자, *args, **kwargs 순으로 정의를 해줘야한다.


docstring

파이썬 문서화에 관련된 부분. 일반 주석은 #을 사용하지만, 모듈과 클래스와 메소드에 사용하는 주석의 형태는 따로 있다. 이것을 doctstring 이라고 한다.


docstring 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
"""
	모듈 (파이썬 파일) 최상단에 이런 형식으로 주석을 달아주세요. 
Usage: 
	python my_test.py <param>
"""

class TestClass: 
"""
클래스 아래에 이런 형식으로 주석을 달아주세요.
"""

	def test_method():
		"""
		함수 아래에 이런 형식으로 주석을 달아주세요.		
		"""

		pass

docstring을 이용해서 주석을 달아두면 코드에서 help 함수를 사용해 접근을 할 수 있다.

내가 지금 사용하는 클래스가 뭘 하는 애인지 해당 클래스 파일을 읽지 않아도 콘솔에 입력만 하면 볼 수 있다는 장점이 있다.


help 함수로 docstring 접근 예시

1
2
help(TestClass)
TestClass.__doc__

라인 유지하기

PEP 에 따르면 파이썬은 한 줄에 80글자를 넘으면 안된다. 가독성이 제일 중요한 언어에서 가독성이 떨어지기 때문이다. 그래서 긴 문장을 사용해야 할 때에는 백슬래시(\)로 라인을 끊어준다.


백슬래시 예시

1
2
3
4
5
6
7
8
test = "this" + \ 
			"is very very" + \ 
			"long long line" 

# 추천하지 않는 라인 끊는 방법
test = ""
test += "is very very" 
test += "long long line"

일등 시민 : 함수

함수는 뷸변하기 때문에 딕셔너리의 키로 사용할 수 있다.

함수를 변수에 할당할 수 있고, 다른 함수에서 이를 인자로 쓸 수 있으며, 함수에서 함수를 반환할 수 있다. 파이썬에서 괄호 ()는 함수를 호출 한다는 의미로 사용되고, 괄호가 없으면 함수를 객체 처럼 간주한다.


함수에서 함수를 반환하는 예시

1
2
3
4
5
6
7
8
def run_something_with_args(func, arg1, arg2): 
	func(arg1, arg2)

def add_args(arg1, arg2): 
	return arg1 + agr2

>> run_something(add_agrs, 5, 8 )
14

  • 예제에서 run_something_with_args로 전달된 add_args 는 괄호 없이 객체처럼 취급되어 func 매개변수로 할당된다.
  • 뒤에 괄호 () 가 붙은 func 는 전달 받은 arg1, arg2를 매개변수로 해 함수를 호출한다.

내부 함수

먼저 읽으면 좋을 자료

함수 안에 또 다른 함수를 정의한다. 함수를 global scope 으로부터 완전히 숨겨 encapsulation을 하거나, 복잡한 작업을 하기 위해 Helper 함수를 만들어야 할 때 내부함수를 쓴다.


내부 함수 예시

1
2
3
4
5
6
7
def increment(number): 
	def inner_increment(): 
		return number + 1
	return inner_increment()

>> increment(10)
11

위처럼 작성을 하면 inner_increment 함수를 어디에서도 호출을 할 수 없다.


내부 함수를 이용해 Helper를 만든 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def factorial(number):
	if not isinstance(number, int):
		raise TypeError("Sorry. 'number' must be an integer.")
	if number < 0:
		raise ValueError("Sorry. 'number' must be zero or positive.")

	def inner_factorial(number):
		if number <= 1:
			return 1
		return number * inner_factorial(number - 1)

	return inner_factorial(number)

그런데 내부함수로 Helper 함수를 만들기보다는 private 으로 Helper 함수를 만드는 것을 권장한다. private helper 함수가 훨씬 더 코드 읽기가 편하고 같은 모듈이나 클래스에서만 재사용이 가능하기 때문이다.


private을 이용해 Helper를 만든 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
def factorial(number):
	if not isinstance(number, int):
		raise TypeError("Sorry. 'number' must be an integer.")
	if number < 0:
		raise ValueError("Sorry. 'number' must be zero or positive.")

	return _factorial(number)

def _factorial(number): 
	if number <= 1:
		return 1
	return number * inner_factorial(number - 1)

클로저

클로저는 바깥 함수로부터 전달된 변수값을 저장하고, 변경을 할 수 있는 함수이다. 파이썬에서 함수를 변수에 할당할 수 있는 이유도 클로저 기능을 지원하기 때문이다.


클로저 예시 generate_power

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# closure factory function
def generate_power(exponent):
    def power(base):
        return base ** exponent
    return power

raise_two = generate_power(2) 

>> raise_two(4) 
16 
>> raise_two(5) 
25

클로저 호출 과정 상세 보기

클로저의 개념이 처음이다보니 제대로 이해가 가지 않아 print로 디버깅을 해가며 이해를 진행했다.


클로저 예시 generate_power_with_debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def generate_power_with_debug(exponent):
    print(f'closure generated, passed exponent {exponent}')

    def power(base):
        print(f'inner function in closure. passed base {base}')
        return base ** exponent

    return power

# closure 생성
raise_two = generate_power_with_debug(2)

# closure 호출
print(f'result of closure : {raise_two(4)}')

# 콘솔에 출력된 결과 
closure generated, passed exponent 2
inner function in closure. passed base 4
result of closure : 16

generate_power 호출 과정 (1)

1
raise_two = generate_power_with_debug(2)) 
  1. generate_power_with_debug 로 변수 exponent에 2를 넣어 클로저 생성한다.
  2. 클로저는 매번 호출될 때마다 새로운 클로저를 생성한다.
  3. 내부 함수 power은 호출이 되지 않고, 새로운 power 인스턴스를 생성해 리턴이 된다.
    1. 리턴값이 함수라는 얘기다.
    2. power를 리턴할 때 power의 surrounding state 를 스냅셧으로 남긴다. 여기에는 exponent 변수가 포함되어있다.

generate_power 호출 과정 (2)

1
print(f'result of closure : {raise_twon(4)}')
  1. generate_power_with_debug 클로저를 호출한다.
  2. 클로저를 호출함에 따라 변수 base에 4를 넣어 내부함수 power가 호출한다.
  3. power는 클로저가 리턴되었을 때 함께 넘어왔던 surrounding state의 스냅샷에 저장이 된 exponent를 이용한다.
  4. power 결과를 리턴한다.

클로저 호출 시나리오 총정리

Q: 어떻게 내부함수를 호출할 때 외부함수의 값에 접근을 할까?
A: 클로저를 생성할 때 내부함수를 리턴하는데, 이때 외부함수의 상태 스냅샷을 함께 리턴해주기 때문이다.
  • 클로저를 구분할 수 있는 부분은 내부함수를 괄호() 로 호출하지 않다는 것이다. 예제에서 power를 리턴하기만 하는데, 이렇게 리턴을 하면 exponent 값을 저장한 power 함수의 복사본을 주게 된다.
  • 복사본을 할당 받은 변수 raise_two를 실제로 매개변수를 넣고 호출한다.
  • 매개변수는 내부함수인 power 의 base 와 맵핑이 된다.

클로저로 권한 확인 함수 구현

클로저로 구현한 권한 확인 함수

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def has_permission(page):
    def permission(username):
        if username.lower() == "admin":
            return f"'{username}' has access to {page}."
        else:
            return f"'{username}' doesn't have access to {page}."
    return permission

# 선언
check_admin_page_permision = has_permission("Admin Page")

>>> check_admin_page_permision("admin")
"'admin' has access to Admin Page."

>>> check_admin_page_permision("john")
"'john' doesn't have access to Admin Page."

데코레이터

데코레이터는 callable(함수, 메소드, 클래스)를 인자로 받고, 다른 callable을 리턴한다(내부함수).

생김새와 위치는 자바의 어노테이션과 동일하다. 데코레이션을 사용하면 이미 존재하고 있던 함수에 별도의 수정사항없이 액션을 추가 할 수 있다.


데코레이터 사용 시나리오

  • 함수를 인자로 받는 callable을 선언한다.
  • 인자로 받은 함수를 호출한다.
  • 추가 액션이 있는 다른 함수를 리턴한다.

데코레이터 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def example_decorator(func): 
	def _add_messages(): 
		print('This is my first decorator')
		func()
		print('bye') 
	# 데코레이터도 클로저처럼 내부함수를 괄호()로 호출하지 않는다. 
	return _add_messages

# greet = example_decorator(greet) 과 동일하다. 클로저 생성 형태와 동일하다. 
@example_decorator
def greet(): 
	print('Hello World')

>>> greet() 
This is my first decorator 
Hello World 
bye 

이렇게 추가로 액션을 행할 수 있게 해주는 데코레이터는 디버깅, 캐싱, 로깅, 시간측정(timing)에 많이 쓰인다.

데코레이터 디버깅 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def debug(func):
    def _debug(*args, **kwargs):
        result = func(*args, **kwargs)
        print(
            f"{func.__name__}(args: {args}, kwargs: {kwargs}) -> {result}"
        )
        return result
    return _debug

@debug
def add(a, b):
    return a + b

>>> add(5, 6)
add(args: (5, 6), kwargs: {}) -> 11
11

데코레이터로 구현한 generate_power

아까 위에서는 클로저로 generate_power를 구현했는데 이번에는 데코레이터로 구현을 했다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
def generate_power(exponent):
    def power(func):
        def inner_power(*args):
            base = func(*args)
            return base ** exponent
        return inner_power
    return power

@generate_power(2)
def raise_two(n):
    return n

>>> raise_two(7)
49

@generate_power(3)
def raise_three(n):
    return n

>>> raise_three(5)
125

데코레이터 호출 과정 상세 보기


데코레이터로 구현한 generate_power_with_debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def generate_power_with_debug(exponent):
    print(f'closure is generated, passed exponent : {exponent}')

    def power(func):
        print(f'inner function in closure. passed func : {func}')

        def inner_power(*args):
            print(f'inner function in power. passed args : {args}')
            base = func(*args)
            return base ** exponent
        return inner_power
    return power

# closure 생성
# raise_two = generate_power_with_debug(2) 와 동일 하다.
@generate_power_with_debug(2) # power()를 리턴
def raise_two(n): # power()를 호출, inner_power()를 리턴
    return n

print(f'result of closure : {raise_two(7)}')

# 콘솔에 출력된 결과 
closure is generated, passed exponent : 2
inner function in closure. passed func : <function raise_two at 0x100e2dee0>
inner function in power. passed args : (7,)
result of closure : 49

generate_power 호출 과정 (1)

1
2
3
@generate_power_with_debug(2)
def raise_two(n): 
	return n 
  1. raise_two = generate_power_with_debug(2) 와 동일하다.
  2. @generate_power_with_debug 데코레이터는 exponent 값을 포함한 내부함수 power를 리턴한다.
  3. raise_two가 선언되면서 power도 호출이 된다.
  4. power는 func를 포함한 내부험수 inner_function을 리턴한다. 여기에서도 inner_function은 호출되지 않고, 새로운 인스턴스를 생성해 리턴이 된다.

generate_power 호출 과정 (2)

1
print(f'result of closure : {raise_two(7)}')
  1. raise_two를 호출하면서 클로저를 호출한다.
  2. 클로저 호출함에 따라 변수 *args에는 raise_two 함수에 전달된 인자 7이 전달된다. 이또한 스냅샷으로 외부 state를 저장했기 때문이다. 2-1. *args는 함수에 전달되는 모든 인자들을 뜻하고, **kwargs는 위치 지정된 모든 인자들을 뜻한다.
  3. inner_power 결과를 리턴한다.

클로저 VS 데코레이터

  • 데코레이터는 클로저를 반환한다.
  • 클로저는 데코레이터에 의해 반환된다.

이름에 ___사용

먼저 읽으면 좋을 자료


_ 와 __ 사용 예시

1
2
3
4
5
6
7
_foo # single leading underscore 
foo_ # single trailing underscore 

_ # single underscore 

__foo__ # double leading and trailing underscore 
__foo # double leading underscore

상세 사용 예시

name e.g. usage
single leading underscore _foo - private(internally) 하게 사용이 됨을 나타낸다.
- 여전히 외부에서 접근이 가능하기 때문에 문맥적 힌트에 가깝다.
single trailing underscore foo_ - 파이썬에서 이미 선점한 키워드를 사용할 때 혼선을 피하기 위한 방법이다.
e.g. type_, from_
single underscore _ - 사용하지 않은 변수들을 담아두는 용도로 쓴다.
e.g. _ = return_something(),
- 숫자가 길어질 때 혼선을 방지하기 위해 쓴다.
e.g. 1000 → 1_000
double leading and trailing underscore __foo__ - dunder method 라고 한다.
- 파이썬에서 이미 선점한 특수 목적 전역 클레스 메소드다.
double leading underscore __foo - 부모-자식 필드 이름을 구분하기 위해 사용되는 것으로 파악했다.
- 실 사용이 거의 없을 거 같다.

파이 포장하기: 모듈, 패키지, 프로그램

모듈

파이썬을 사용하다보면 모듈이라는 단어가 자주 나오는데 여기서 모듈이란 단순히 파이썬 코드가 들어가있는 파일을 뜻한다.


패키지

파이썬을 좀 더 확장 가능한 어플리케이션으로 만들기 위해서는 모듈을 패키지라는 파일 계층구조로 구성해야 한다. __init__.py 는 파일 내용을 비워놔도 되지만, 파이썬은 이 파일을 포함하는 디렉터리를 패키지로 간주 하기 때문에 패키지로 사용하고 싶다면 꼭 만들어둬야 한다.

파이썬에서 batteries included 철학은 유용한 작업을 처리하는 많은 표준 라이브러리 모듈들이 내장이 되어있다는 뜻이다.


Deque = Stack + Queue

파이썬 list는 left end의 pop()append()가 빠르지가 않기 때문에 left-end와 right-end 모두 빠르고 메모리를 효과적으로 사용하기 위해 데크를 제공한다.

list의 right-end 연산 속도는 O(1)이지만, left-end 연산 속도는 O(n)이다.


Deque 구현체

  • Deque 는 Stack과 Queue의 기능을 가졌다.
  • 출입구가 양 끝에 있는 Queue다.(double-ended queue의 구현체이다)
  • Deque는 양 끝으로부터 항목을 추가하거나 삭제할 때 유용하게 쓰인다.
  • popleft()는 left-end를 제거해서 반환하고, pop()은 right-end를 제거해서 반환한다.

Deque 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from collections import deque

numbers = deque([1,2,3,4]) 

>>> numbers.popleft()
1 
>>> numbers.popleft()
2 
>>> numbers 
deque([3,4])

>>> numbers.appendleft(2)
>>> numbers.appendleft(1) 
>>> numbers
deque([1,2,3,4])

Deque의 흥미로운 점

  • 최대 길이 (maximum lenght)를 지정할 수 있다.
  • 한 쪽에서 데이터를 넣어 큐가 꽉 차게 되면 자동으로 다른 쪽에 있는 아이템을 버린다.
  • 이러한 기능으로 인해 이전 0회의 기록을 남기기 와 같은 요구사항이 있을 때 활용하기가 용이하다.
  • [더 많은 Deque 사용법] 에서 더 많은 용도를 확인할 수 있다.

히스토리 남기기 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from collections import deque

sites = (
    "google.com",
    "yahoo.com",
    "bing.com"
)

pages = deque(maxlen=3)
pages.maxlen

for site in sites:
    pages.appendleft(site)

>>> pages
deque(['bing.com', 'yahoo.com', 'google.com'], maxlen=3)

pages.appendleft("facebook.com")
>>> pages
deque(['facebook.com', 'bing.com', 'yahoo.com'], maxlen=3)

pages.appendleft("twitter.com")
>>> pages
deque(['twitter.com', 'facebook.com', 'bing.com'], maxlen=3)

Linux의 tail 모방 예시

1
2
3
4
5
6
7
8
from collections import deque

def tail(filename, lines=10):
    try:
        with open(filename) as file:
            return deque(file, lines)
    except OSError as error:
        print(f'Opening file "{filename}" failed with error: {error}')

thread-safe

  • CPython에서 dequeappend(), appendleft(), pop(), popleft(), len()은 thread-safe 하게 만들어졌기 때문에 멀티쓰레드 환경에서 deque를 사용하기 좋다.
    • CPyton은 C로 구현한 파이썬으로, 가장 많이 사용되고 있는 파이썬 구현체다. 오픈소스로 관리가 되고 있다. [깃허브]