akxltmzk
2/17/2020 - 5:38 AM

라이브러리와 툴,팁(1)

// contextlib.contextmanager

import contextlib

# 데코레이터를 가독성 높게 사용할 수 있다.

@contextlib.contextmanager
def tag(name):
    print('<{}>'.format(name))
    yield
    print('<{}>'.format(name))

#방법 1
@tag('h2')
def f(content):
    print(content)

f('test')

"""
출력 :
<h2>
test
<h2>
"""

#방법2

with tag('h2'):
    print('test')

"""
출력 :
<h2>
test
<h2>
"""

#방법3(확장해보자!!)

def f():
    print('확장가능하다')
    with tag('h2'):
        print('h2:test')
    with tag('h3'):
        print('he:test')
f()
"""
출력:
확장가능하다
<h2>
h2:test
<h2>
<h3>
he:test
<h3>
"""
================================================================================
//contextlib.ContextDecorator

import contextlib

class tag(contextlib.ContextDecorator):
    def __init__(self,name):
        self.name = name
        self.start_tag = '<{}>'.format(name)
        self.end_tag = '<{}>'.format(name)

    def __enter__(self): #클래스가 들어올때 실행
        print(self.start_tag)

    def __exit__(self, exc_type, exc_val, exc_tb): #클래스 끝날때 실행
        #에러 헨들링도 가능하다!
        print(exc_type) #<class 'Exception>
        print(exc_val) # err
        print(exc_tb) #<traceback object at 0x00000215D7A76BC8>

        print(self.end_tag)

with tag('h2'):
    raise Exception('err') #에러헨들링 테스트를 해보자!
    print('test')
================================================================================
//contextlib.suppress
import contextlib
import os

try:
    os.remove('test.txt')
except FileNotFoundError:
    pass
#위를 더 깔끔하게 적을 수 있다.
with contextlib.suppress(FileNotFoundError):
    os.remove(('test.txt'))
    
================================================================================
//contextlib.redirect_stdout / contextlib.redirect-sterr
import contextlib
import logging

with open('stdout.log','w') as f: #stdout.log파일을 생성한다.
    with contextlib.redirect_stdout(f):
        print('hello') #stdout.log파일에 hello라고 입력.(터미널에는 출력안됨)

with open('stderr.log','w') as f: #stderr.log파일을 생성한다.
    with contextlib.redirect_stderr(f):
        logging.error('Error!') #로깅 정보가 stderr.log파일에 입력 된다.
        """
        이렇게 쓰는 이유는  help 정보를 다른 곳에 적거나 일부 logging정보를 다른 곳에 적고싶을때 많이 쓴다.
        """

================================================================================
//contextlib.ExitStack

import contextlib

def is_ok_job():
    try:
        print('do something')
        #일부러 에러를 발생시켜보자
        raise Exception('error')
        return True
    except Exception:
        return False

def cleanup():
    print('clean up')
def cleanup2():
    print('clean up2')


with contextlib.ExitStack() as stack:
    stack.callback(cleanup2) #stack에 콜백시키면 이 함수의 마지막에 불리게 된다!!
    stack.callback(cleanup)

    @stack.callback #위와 같음 ==  stack.callback
    def cleanup3():
        print('clean up3')

    is_ok = is_ok_job()
    print('more task')

    # 만약에 에러가 없다면. stack안에 있는 함수를 전부 pop시킨다(안부른다)
    # 때문에 is_ok가 True라면 위의 stack.callback이 불리지 않는다!
    if is_ok:
        stack.pop_all()

"""
출력 :
do something
more task
clean up3
clean up
clean up2
"""

================================================================================
//collections.ChainMap

import collections

a = {'a':'a','c':'c','num':0}
b = {'b':'b','c':'cc'}
c = {'b':'bbb','c':'ccc'}

m = collections.ChainMap(a,b,c)
print(m) #ChainMap({'a': 'a', 'c': 'c', 'num': 0}, {'b': 'b', 'c': 'cc'}, {'b': 'bbb', 'c': 'ccc'})
print(m.maps) #[{'a': 'a', 'c': 'c', 'num': 0}, {'b': 'b', 'c': 'cc'}, {'b': 'bbb', 'c': 'ccc'}]
m.maps.reverse()
print(m.maps) #[{'b': 'bbb', 'c': 'ccc'}, {'b': 'b', 'c': 'cc'}, {'a': 'a', 'c': 'c', 'num': 0}]
print(m['c']) #ccc ->가장 앞에있는 사전형의 c를 가져온다
m.maps.insert(0,{'c':'cccccc'})
print(m.maps) #[{'c': 'cccccc'}, {'b': 'bbb', 'c': 'ccc'}, {'b': 'b', 'c': 'cc'}, {'a': 'a', 'c': 'c', 'num': 0}]
print(m['c']) #cccccc
del m.maps[0]
print(m.maps) #[{'b': 'bbb', 'c': 'ccc'}, {'b': 'b', 'c': 'cc'}, {'a': 'a', 'c': 'c', 'num': 0}]
print(m['c']) #ccc
m['b'] = 'BBBBBBB'
print(m.maps) #[{'b': 'BBBBBBB', 'c': 'ccc'}, {'b': 'b', 'c': 'cc'}, {'a': 'a', 'c': 'c', 'num': 0}]
------------------------------------
import collections

a = {'a':'a','c':'c','num':0}
b = {'b':'b','c':'cc'}
c = {'b':'bbb','c':'ccc'}

class DeepChainMap(collections.ChainMap):
    def __setitem__(self, key, value):
        for mapping in self.maps:
            if type(mapping[key]) is int and mapping[key] < value:
                mapping[key] = value
            return
        self.maps[0][key] = value

m = DeepChainMap(a,b,c)

#TEST 1
m['num'] = 1
print(m) #DeepChainMap({'a': 'a', 'c': 'c', 'num': 1}, {'b': 'b', 'c': 'cc'}, {'b': 'bbb', 'c': 'ccc'})

#TEST2(TEST1을 지우고 테스트 하세요)
m['num'] = -1
print(m) ##DeepChainMap({'a': 'a', 'c': 'c', 'num': 0}, {'b': 'b', 'c': 'cc'}, {'b': 'bbb', 'c': 'ccc'})

================================================================================
//collections.defaultdict

import collections

d = {}
l = ['a' , 'a' , 'a' , 'b' , 'b' , 'c']

for word in l :
    if word not in d:
        d[word] = 0
    d[word] += 1

print(d) #{'a': 3, 'b': 2, 'c': 1}

#-- 위와 같음---#

d = {}
l = ['a' , 'a' , 'a' , 'b' , 'b' , 'c']
for word in l:
    d.setdefault(word,0)
    d[word] += 1
print(d) #{'a': 3, 'b': 2, 'c': 1}

#-- 위와 같음---#
d = collections.defaultdict(int) #제일 처음은 디폴트로 0이 들어간다
l = ['a' , 'a' , 'a' , 'b' , 'b' , 'c']
for word in l:
    d[word] += 1
print(d) #defaultdict(<class 'int'>, {'a': 3, 'b': 2, 'c': 1})

#-- collection.default 다른 예제---#
d = collections.defaultdict(set)
s = [('red' , 1), ('blue' , 2), ('red',3), ('blue',4), ('red',1), ('blue',4)]
for k,v in s:
    d[k].add(v)
print(d) # {'red': {1, 3}, 'blue': {2, 4}})

================================================================================
//collections.Counter

l = ['a' , 'a' , 'a' , 'b' , 'b' , 'c']
c = collections.Counter()
for word in l:
    c[word] += 1

print(c) #Counter({'a': 3, 'b': 2, 'c': 1})
print(c.most_common(1)) #[('a', 3)]  -> 랭킹 1까지(순위 같은거 매길때 유용하게 사용된다.)
print(c.most_common(2)) #[('a', 3), ('b', 2)] ->랭킹 2까지
print(c.values()) #dict_values([3, 2, 1])
print(sum(c.values())) #6

================================================================================
//collections.deque

import collections
import queue

collections.deque

q = queue.Queue()
lq = queue.LifoQueue()
l = []
d = collections.deque() #list보다 훨씬 빠르고, 메로리 부담을 던다.

for i in range(3):
    q.put(i)
    lq.put(i)
    l.append(i)
    d.append(i)

for _ in range(3):
    #선입선출
    print('FIFO quene = {}'.format(q.get()))
    #후입선출
    print('LIFO quene = {}'.format(lq.get()))

    # 선입선출
    print('list       = {}'.format(l.pop()))
    print('deque      = {}'.format(d.pop()))

    #후입선출
    # print('list       = {}'.format(l.pop(0)))
    # print('deque      = {}'.format(d.popleft()))

    print()

d.rotate() #순서 뒤집기
d.extendleft('x') #왼쪽에 x 추가
d.extend('y') #오른쪽에 y추가

"""
출력 :

FIFO quene = 0
LIFO quene = 2
list       = 2
deque      = 2

FIFO quene = 1
LIFO quene = 1
list       = 1
deque      = 1

FIFO quene = 2
LIFO quene = 0
list       = 0
deque      = 0

"""
================================================================================
//collections.deque