2022년 10월 26일 수요일

파이썬 textwrap (indent, 정렬, 줄바꿈)

 이 textwrap 모듈은 텍스트를 핸들링할때 indentation을 넣거나 빼거나, 글자수를 터미널 너비에 맞춘다거나 할때 유용합니다. 즉, pretty-print가 필요한 경우, 텍스트를 핸들링할때 유용합니다. 많은 텍스트 편집기 및 워드 프로세서에서 볼 수 있는 단락 줄 바꿈 또는 채우기 기능과 유사한 프로그래밍 기능을 제공합니다.

Filling Paragraphs

fill 함수에 대해 알아봅니다 fill() 함수는 텍스트를 width에 맞게 잘라주는 역할을 합니다.

# 실제 함수원형은 좀더 길지만, 아래 형태로 확인합니다
textwrap.fill(text: str, width: int) -> str 
import textwrap

text = '''
    I have two sisters who have made their careers in engineering. My first sister has
    a doctorate in civil engineering. She works in Australia designing mine tunnels for
    a raw materials company. My second sister earned a master's degree in electrical
    engineering. She works in the United States designing communication equipment.
    In summary, my two sisters both have graduate degrees in engineering and have
    careers in engineering.
'''

print (textwrap.fill(text,width=50))
'''
 I have two sisters who have made their careers in
engineering. My first sister has a doctorate in
civil engineering. She works in Australia
designing mine tunnels for a raw materials
company. My second sister earned a master's degree
in electrical engineering. She works in the United
States designing communication equipment. In
summary, my two sisters both have graduate degrees
in engineering and have careers in engineering.
'''

이제 텍스트가 왼쪽 정렬되지만 첫 번째 줄은 들여쓰기를 유지하고 각 후속 줄의 앞부터 공백이 단락에 포함됩니다.

textwrap 모듈은 예쁜 인쇄가 필요한 상황에서 출력할 텍스트의 형식을 지정하는 데 사용할 수 있습니다. 프로그래밍 방식을 제공합니다. 많은 텍스트 편집기에서 볼 수 있는 단락 줄 바꿈 또는 채우기 기능과 유사한 기능입니다.

Indentation 제거

이전 예제에는 출력에 포함된 탭과 추가 공백이 포함되어 있으므로 형식이 매우 명확하지 않습니다. 샘플 텍스트의 모든 줄에서 공통 공백 접두사를 제거하면 더 나은 결과를 얻을 수 있으며 코드 서식 자체를 제거하면서 Python 코드에서 직접 독스트링 또는 포함된 여러 줄 문자열을 사용할 수 있습니다. 샘플 문자열에는 이 기능을 설명하기 위해 도입된 인위적인 들여쓰기 수준이 있습니다. dedent를 사용하면 공백을 제거해 줍니다.

'''
I have two sisters who have made their careers in engineering. My first sister has
a doctorate in civil engineering. She works in Australia designing mine tunnels for
a raw materials company. My second sister earned a master's degree in electrical
engineering. She works in the United States designing communication equipment.
In summary, my two sisters both have graduate degrees in engineering and have
careers in engineering.
'''

아래 예제도 참고 합니다. indent가 들쭉날쭉 한 경우에도 이 공백을 댕겨줍니다. 다만, 실제 paragraph상의 indent를 제거하지 않습니다.

text2= '''
        I have two sisters who have made their careers in engineering. My first sister has
    a doctorate in civil engineering. She works in Australia designing mine tunnels for
    a raw materials company. My second sister earned a master's degree in electrical
    engineering. She works in the United States designing communication equipment.
        In summary, my two sisters both have graduate degrees in engineering and have
    careers in engineering.
'''

print (textwrap.dedent(text2))

'''
    I have two sisters who have made their careers in engineering. My first sister has
a doctorate in civil engineering. She works in Australia designing mine tunnels for
a raw materials company. My second sister earned a master's degree in electrical
engineering. She works in the United States designing communication equipment.
    In summary, my two sisters both have graduate degrees in engineering and have
careers in engineering.
'''

Dedent 와 Fill 함수 조합

다음은 dedent된 텍스트를 fill로 맞추어 봅니다. 위의 Text2 예제를 그대로 씁니다. dedent로 공백을 걷어내고, strip() 으로 좌우 여백을 완전히 잘라줍니다. 그리고 fill을 사용하면 width에 맞게 다시 text가 정렬이 됩니다.

text3= textwrap.dedent(text2).strip()
text4= textwrap.fill(text3,width=60)
print (text4)
'''
I have two sisters who have made their careers in
engineering. My first sister has a doctorate in civil
engineering. She works in Australia designing mine tunnels
for a raw materials company. My second sister earned a
master's degree in electrical engineering. She works in the
United States designing communication equipment.     In
summary, my two sisters both have graduate degrees in
engineering and have careers in engineering.
'''

Indents

때로는 문장 전체에 앞에 공백등을 넣어야 할때가 있습니다. 원래 문장에 4칸의 공백을 넣어봅니다.

text5='''
I have two sisters who have made their careers in
engineering. My first sister has a doctorate in civil
engineering. She works in Australia designing mine tunnels
for a raw materials company. My second sister earned a
master's degree in electrical engineering. She works in the
United States designing communication equipment.     In
summary, my two sisters both have graduate degrees in
engineering and have careers in engineering.
'''
print (textwrap.indent(text5,' '*4))

'''
    I have two sisters who have made their careers in
    engineering. My first sister has a doctorate in civil
    engineering. She works in Australia designing mine tunnels
    for a raw materials company. My second sister earned a
    master's degree in electrical engineering. She works in the
    United States designing communication equipment.     In
    summary, my two sisters both have graduate degrees in
    engineering and have careers in engineering.
'''

위와 같이 indent가 잘 삽입되었습니다.

이상으로 textwrap이 잘 동작 하는것을 확인해보았습니다. 이 textwrap은 종종 텍스트를 화면에 안잘리고 pretty-print하는 용도로 사용합니다.

2022년 10월 24일 월요일

파이썬 정수 나눗셈 (/, // 차이)

 이건 버젼별로 살짝 다른 부분이 있습니다. 파이썬 2.7에서 다음과 같이 나누기를 하면 나눗셈 연산이 버림을 기본으로 하는 정수 연산으로 수행이 됩니다. 정수끼리 나눗셈을 하면 정수를 리턴하고 , 실수가 중간에 끼어있으면 실수값이 리턴됩니다.

이게 C와 비슷한데 정수끼리 나눗셈을 할 경우 기본적으로 정수로 가정해서 연산을 하고는 합니다. 실수형을 리턴받기 위해서는 아래와 같이 하나를 실수로 변경(2 -> 2.0)하면 결과값이 실수가 리턴이 됩니다.

# python 2.7 
4 / 2 
>> 2 # int
4 / 2.0
>> 2.0 # float

파이썬 3에서는 / 연산은 float 을 리턴합니다. 즉, 나눗셈연산이 기본적으로 실수연산으로 전환이 됩니다. 정수연산을 하기위해서는 // 을 사용해서 정수연산을 수행합니다.

# python 3
4 / 2 
>> 2.0 # float
4 // 2
>> 2 # int

/ 연산자를 floating으로 전환 한다해도 정수연산이 필요한 경우가 여전히 존재하므로, 이때는 //로 정수 나눗셈연산으로 처리를 할 수가 있습니다.

이걸 왜 구분해야하는냐 이 float 연산과 int 연산은 속도 차이가 좀 있습니다. 보통은 나눗셈연산이 오래 걸리기때문에, 정수로 처리할 수 있는 것들은 정수로 처리하는게 수행시간 측면에서 유리합니다 그리고 이런 나눗셈연산자는 % 와 같은 나머지 연산자와 보통 정수와 같이 핸들링하는 경우가 많습니다.

그래서 이 차이를 알고 사용하는 것이 좀더 최적화된 구조를 만드는데 도움이 되리라 생각합니다.

이번에는 정수 나눗셈연산에 정리해보았습니다. 이부분이 실제로 작성하다보면 종종 놓치는 부분이기도 하니, 이 정수 나눗셈이 버젼별로 다름을 살짝 인지하고 정수 나눗셈이랑 실수 나눗셈을 구분해서 사용하면 좋을듯합니다.

2022년 10월 21일 금요일

파이썬 zfill 문자열 앞에 0 채우기 (이진수 만들기)

이 파이썬의 zfill함수는 이진수를 만들때 유용합니다. 뿐만 아니라, 전화번호나 날짜등을 표기할때도 유용합니다. 이번에는 주로 바이너리를 표현하는 방법에 관련해서적어 보려고 합니다.

먼저 8을 이진수로 변경해봅니다.

print (bin(8))
# 0b1000

앞의 0b가 거슬립니다. 문자열이니 2번째 다음부터 가져오면 이진수만 떼어내 봅시다.

print (bin(8)[2:])
# 1000

이번엔 8이외에 여러 숫자들을 이진수로 바꾸어 봅니다.


print (bin(0)[2:])
print (bin(1)[2:])
print (bin(2)[2:])
print (bin(3)[2:])
print (bin(6)[2:])
print (bin(7)[2:])
print (bin(8)[2:])

# 0
# 1
# 10
# 11
# 110
# 111
# 1000

이진수가 만들어지지만, 뭔가 좀 비뚤빼뚤합니다. 이때 자리를 맞추기 위해서 zfill함수를 사용합니다.

print (bin(0)[2:].zfill(4))
print (bin(1)[2:].zfill(4))
print (bin(2)[2:].zfill(4))
print (bin(3)[2:].zfill(4))
print (bin(6)[2:].zfill(4))
print (bin(7)[2:].zfill(4))
print (bin(8)[2:].zfill(4))

# 0000
# 0001
# 0010
# 0011
# 0110
# 0111
# 1000

인제 뭔가 자리수가 맞는것 같습니다. 이렇게 표현하는 것이 뭔가 줄이 맞으니, 가독성면에서도 좋습니다 이 zfill 함수는 위와 같이 N자리 보다 작은 스트링을 앞쪽에 0을 붙여서 채웁니다.

이 방법은 위와 같이 파이썬으로 이진수 표현할 때 유용합니다. 

2022년 10월 20일 목요일

파이썬 collections Counter

Collection의 Counter 함수는 array나 list에 해당 값이 얼마나 나왔는지 세는 함수입니다.

이 Counter object는 사실 dictionary를 inherit 한것이라,, dictionary형태를 지니고 있습니다

아래는 예제입니다.

from collections import Counter
sample = Counter([1,2,3,4,2,3,4,1,2,3])
print (sample[4])  # 2

위 결과는 4의 개수를 센 값이 나옵니다.

Counter는 실제 excel같은데서 많이들 사용하는 함수와 같은 유형이라, 생각보다 유용한 경우가 많이 있습니다.

counter 함수를 사용해 리스트의 개수세기

collections.Counter(a) : a에서 요소들의 개수를 세어, 딕셔너리 형태로 반환합니다. {문자 : 개수} 형태

import collections
b = [1,3,4,3,2,3,5,1,2,3,2,3,9]
a = [1,2,3,4,3,2,2,1,5,3,1,3,4,2,3]
print(collections.Counter(a) , collections.Counter(b))

# Counter({3: 5, 2: 4, 1: 3, 4: 2, 5: 1}) Counter({3: 5, 2: 3, 1: 2, 4: 1, 5: 1, 9: 1})

counter - 연산

counter 함수로 구한 딕셔너리의 값(value)끼리 연산이 가능합니다. 연산은 + , - , &(교집합), |(합집합) 네가지가 가능합니다. 서로 빼기 연산을 했을때 , 음수 값이 나오지는 않습니다. a list의 2, 3번이 나오지 않습니다

import collections
a = [1,1,1,1,2,3,1,1]
b = [1,1,2,2,2,2,3,3,3]

print(collections.Counter(a) - collections.Counter(b))
print(collections.Counter(a) + collections.Counter(b))
print(collections.Counter(a) & collections.Counter(b))
print(collections.Counter(a) | collections.Counter(b))

# Counter({1: 4})
# Counter({1: 8, 2: 5, 3: 4})
# Counter({1: 2, 2: 1, 3: 1})
# Counter({1: 6, 2: 4, 3: 3})

most_common() 함수 - 최빈값 구하기

most_common([n]) : 많이나오는 순서대로 상위부터 n개까지를 출력 (리스트에 담긴 튜플형태로)

import collections
a  = [1,1,1,2,3,2,3,1,4,2,2,2,45,9]

print(collections.Counter(a).most_common(3))
 
# [(2, 5), (1, 4), (3, 2)]

elements() : 모든 element의 list를 출력

import collections
a  = [1,1,1,2,3,2,3,1,4,2,2,2,45,9]

print(list(collections.Counter(a).elements()))
 
# [1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 4, 45, 9]

이상으로 collections의 Counter 메소드들을 정리해보았습니다.




2022년 10월 19일 수요일

파이썬 정규식, Greedy Non Greedy탐색(최소일치/최대일치)

 이번엔 파이썬의 정규식(RegEx)중 Greedy match/Non-Greedy match부분을 설명합니다.

아래와 같은 예제에서 첫번째 괄호만 매치를 시키자고 하고합니다.

text = '(Machester 10) (London 20) (Seoul 30) (Tokyo 40)'

그렇지만, 실제로 아래와 같은 예제를 보면, 첫번째 괄호만 매치되는 게 아니라 마지막 괄호까지 전부 매치가 됩니다.

(.*) - Greedy match

(Manchester 10) (London  20) (Seoul 30) (Tokyo 40)

(.*?) - Non Greedy match (Manchester 10) (London  20) (Seoul 30) (Tokyo 40)

이것은 기본적으로 정규식은 Greedy match (최대한 많이 매치)를 하기 때문인데, 이렇게 되면 원래 의도와는 다른 결과가 나올 수 있습니다.

그래서 .* 뒤에 ?를 붙여서 .*? 과 같이 표현합니다. 그러면, 이 탐색자가 가장 많이 매치되는 경우를 찾아주는 것이 아니라 가장 적게 매치되는 경우를 찾아줍니다.

text = '(Machester 10) (London 20) (Seoul 30) (Tokyo 40)'
p = re.compile(r'(\(.*\))')
if m:= p.search(text): print (m.groups()) 
# ('(Machester 10) (London 20) (Seoul 30) (Tokyo 40)',)

p = re.compile(r'(\(.*?\))')
if m:= p.search(text): print (m.groups()) 
# ('(Machester 10)',)

이런 예는 종종 볼수 있습니다. 이것 말고도 HTML, XML match시킬때도 마찬가지 입니다. 또, C-style 주석도 이런 경우 종종 볼수 있습니다. /* ...  */ 와 같이 주석처리를 지우려고 하는데 , 텍스트 파일 전체가 지워지는 경우, 이 그리디매치가 적용이 되어서 그런것입니다.

가장 가까운 괄호와 매치시키고 싶은데, 반복자가 최대한 많이 매치시키려 하기 때문에, 이런 부분의 차이를 알고 있다면 정규식 쓰는데 좀더 활용도를 높일 수가 있습니다.

2022년 10월 17일 월요일

파이썬 이터레이터 - 반복자 클래스


이번 주제는 파이썬 iterator입니다. 이터레이터는 번역한 것보면, 반복자라고도 하죠.
이글은 이전에 작성하다가 나두었던 글인데, 다시 정리해서 올려봅니다

파이썬에서 나오는 ~레이터, 이터레이터, 제네레이터, 데코레이터,, 이중에서 데코레이터는 앞에 두개랑 크게 상관이 없고요,

이터레이터 와 제네레이터가 관련이 좀 있습니다.

iterator를 이야기 하기전에 일단 iterable이라는 말을 집고 갑시다, iterable 이라는 의미는 반복이 가능하다는 의미입니다.

파이썬의 기본 데이터형인 리스트, 튜플, 딕셔너리등이 iterable이라고 합니다.

그럼 이터레이터는?

이것을 실제로 동작을 시키는 부분을 이터레이터라고 합니다, 리스트등을 이터레이터에 넣어서 데이터를 하나씩 빼내어 주는 용도 입니다,

이터레이터는 기본적으로 design pattern에서 많이 사용되는 용어입니다

우리가 어떤 반복문을 돌릴때, 이전에는 주로 for 문을 가지고 반복문을 만드는데, 이것을 좀 더 추상화, 일반화 시킨 개념이 이터레이터입니다, 예전에 C에서 반복문을 짤때는 다음과 같은식으로 시작을 했습니다

 for (i = 0; i < 10 ; i++ )  ; 
     // action by array[i] 

이런식으로 명시적으로 반복문의 구조를 써줬는데,

사이즈로 신경쓸필요 없이 item들을 가져다 달라는 구문입니다. 다른 언어중 foreach 로 시작하는 구문들이 이런 유사한 방식입니다 .

부수적인 정보들 예를 들면, 사이즈등은 신경쓰고 싶지 않고, Array를 줄테니 알아서, 하나씩 빼줘 하는 개념이 이터레이터입니다.

여기서 Array가 List, dictionary, tuple이 되는 것겠죠,

이것이 꼭 정해진 data type이 아니라, 꼭 이 새로운 클래스에서도 iter함수를 구현하면, iterator를 사용할수 가 있습니다.

꼭 배열이 아니라 집합이라든지, 어떤 object건 간에 iterator 를 호출하면, 미리 정의된대로 하나씩 불러오게 할 수 있습니다.

어떤 큰단위가 있고, 하나씩 세부 아이템을 호출하는 용도입니다. 일반적으로 list, dictionary같은 형태는 for문에 넣으면 잘 동작합니다.

아래와 같이 리스트, 딕셔너리, 스트링 전부 잘 이터레이션이 됩니다.

for x in [1,2,3]: # 리스트
    print(x)
for x in (1,2,3): # 튜플
    print(x)
for x in 'abc': # 문자열
    print(x)

여기까지는 큰 문제 없이 몇번 사용하보면 이해가 쉬울것입니다.

그러면, class에도 for문에 적용을 해보고 싶을때가 있습니다. 클래스를 이터러블하게 만드는 방법입니다. iter() 함수를 class 내부에 구현하는 방법입니다.

next 와 StopIteration을 구현을 해줍니다. 이건 제네레이터에서도 나온 문법인데,

먼저 next() 는 다음값을 구해주는 연산입니다. 그리고 StopIteration은 더이상 값이 없을때 호출해 줍니다.

이런 식으로 iteration을 구현할 수 있습니다

예제를 하나 알아봅니다. 무한 수열을 만들어봅니다. 리스트가 작으면 그냥 기입을 하면 되겠는데, 이 끝을 알수 없는 형태가 나오면, 무작정 나열할 수 가 없습니다.

x = [1,2,3,4,......,]  # fail

먼저아래와 같이 무한수열을 class로 선언해 줍니다.

class Infite:
    def __init__(self) -> None:
        pass

a = Infite()

for x in a:
    print (x)

'''
Traceback (most recent call last):
  File "infinite.py", line 10, in <module>
    for x in a:
TypeError: 'Infite' object is not iterable
'''

위 코드를 실행시키면 object가 iterable하지 않다고 에러가 납니다.

이 클래스를 이터러블하게 만들어 봅니다. __iter__와 __next__를 추가해줍니다. __iter__는 iterable할때 초기화 시키는 함수라고 보면 되고, 아래는 직접 자기자신을 리턴하긴 했지만, 내부 리스트나 딕셔너리등을 리턴할 수도 있습니다. 여기서는 0으로 초기화 해줍니다. __next__에서는 리턴값을 더해줍니다. 내부 $self.cnt$에 +1하는 값을 더합니다. 이와 같이 구현할 경우, 종료값이 없으므로 무한수열 입니다. 5에서 break를 걸었는데, break가 없으면 이 루프는 무한으로 동작합니다.

class Infite:
    def __init__(self) -> None:
        self.cnt = 1
       
    def __iter__(self):
        self.cnt =0
        return self
    def __next__(self):
        self.cnt +=1
        return self.cnt

a = Infite()

for i,x in enumerate(a):
    print (x,end=' ')
    if i == 5:
        break
print ('')

# 1 2 3 4 5 6 

여기서 next에 StopIteration을 추가해 봅니다. 아래와 같이 internal counter가 9에 도달할 경우, raise StopIteration을 호출해서, loop를 종료합니다. 실제로 무한루프가 돌면, 끝나지 않으니, 이런 식으로 종료를 시켜줄수 있겠죠. 아래 예제에서는 break구문없이 class내부에서 StopIteration을 호출해서, 마무리를 지어줍니다.

class Infite:
    def __init__(self) -> None:
        self.cnt = 1
       
    def __iter__(self):
        self.cnt =0
        return self
    def __next__(self):
        self.cnt +=1
        if self.cnt == 9:
            raise StopIteration
        return self.cnt

a = Infite()

for i,x in enumerate(a):
    print (x,end=' ')
print ('')

# 1 2 3 4 5 6 7 8

이상으로 이터레이션과 이터러블한 객체를 만드는 법을 정리해보았습니다. 위 예는 단순한 예이지만, 실제로 객체를 이터러블하게 만드는 방법은 실제로도 많이 쓰입니다. 이 내용을 참고하셔서 활용하시면 좋을것 같습니다.