오랜만에 돌아온 모듈 탐구 시간이다. 이번 시간에는 일반 문자열 연산을 조금 더 편리하게 도와주는 string 모듈에 대해서 알아보자

 


제공하는 문자열

1. string.ascii_letters : 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

2. string.ascii_lowercase : 'abcdefghijklmnopqrstuvwxyz'

3. string.ascii_uppercase : 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

4. string.digits : '0123456789'

5. string.hexdigits : '0123456789abcdefABCDEF'

6. string.octdigits :'01234567'

7. string.punctuation : '!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~'

8. string.printable : 출력 가능한 모든 문자열의 조합(ascii_letter + punctuation + digits)

9. string.whitespace : 공백으로 취급되는 모든 문자열(스페이스, 탭, 줄 바꿈, 캐리지 리턴, 세로 탭 및 폼 피드)

 

 

Formatter 클래스

Formatter은 문자열을 리터럴과 치환 필드(중괄호 {})로 표시를 한다. Formatter클래스는 str.format()과 유사하게 사용된다.

 

format 가장 기본적인 사용법은 다음과 같다.

 

>>> "tell me your {}".format("money")
tell me your money

 

 

중괄호 안에는 다음과 같은 문법을 써넣을 수 있으며, 의미는 옆 주석을 확인하면 된다.

"I love {}" ### 일반적인 치환문자{} "I love {0}"와 같은 의미이다.
"I love {} and {}" ### "I lovev {0} and {1}"와 같은 의미이다.
>>> 순서를 지정해줄 수 도 있고, 생략할 수도 있다.

"I have my name {name}" ### 키워드 name으로 지정값을 대입하게 할 수 있다.
"List number 1 is {my_list[0]}" ### my_list의 첫번째 원소값을 대입하게 한다
"Show me your money {0.my_money}" ### 0번째의 원소의 속성값 my_money를 출력하게 한다.
>>> 키워드를 통한 사용도 가능하며 나아가 자료구조의 속성이나 원소를 넣어줄 수 있다.

"I have much fun with {0!s}" ### 0번째 객체의 str()메소드를 호출한다.
"I have much fun with {0!r}" ### 0번째 객체의 repr()메소드를 호출한다.
"I have much fun with {0!a}" ### 0번째 객체의 ascii()메소드를 호출한다.
>>> 객체의 내장함수를 호출할 수 있다.

 

다양한 트릭을 다음처럼 사용할 수 있다.

### Trick 1. 텍스트 정렬
>>> "{:<30}".format("좌로밀착!")
'좌로밀착!                         '

>>> "{:>30}".format("우로밀착!")
'                         우로밀착!'

>>> "{:&^30}".format("가운데정렬!")
'&&&&&&&&&&&&가운데정렬!&&&&&&&&&&&&'


### Trick 2. 진법
>>> my_height = 185

>>> print("키가 어떻게 되세요?")
키가 어떻게 되세요?

>>> "10진수로 : {0:d} 16진수로 : {0:x} 8진수로 : {0:o} 2진수로 {0:b}".format(my_height)
'10진수로 : 185 16진수로 : b9 8진수로 : 271 2진수로 10111001'


### Trick 3. 구분자
>>> money = 100000000
>>> rate = 0.35

>>> "{0:,} {1:.2%}".format(money, rate)
'100,000,000 35.00%'

 

 

Template 클래스

Template 클래스는 대체 문자열 하나를 인자로 생성된다. 사용법이 직관적이니 다음의 코드를 확인하자.

### Template의 사용 ###
>>> my_template=string.Template("$abc is my $hobby")
>>> my_dict = {"abc":"riducto","hobby":"python"}
>>> your_dict = {"abc":"riducto"}

>>> my_template.substitute(my_dict)
'riducto is my python'
>>> my_template.substitute(your_dict)
Traceback (most recent call last):
...
KeyError: 'hobby'
### subsutitube는 템플릿에 지정된 $문자를 키로한 딕셔너리로 치환한다. key가 없을 경우 KeyError Exception

>>> my_template.safe_substitute(your_dict)
'riducto is my $hobby'
### 그런당신을 위해 safe_substitute는 지정문자를 치환에 실패한 경우 내비둔다.

 

* 출처 : docs.python.org/ko/3/library/string.html

 

string — 일반적인 문자열 연산 — Python 3.9.4 문서

string — 일반적인 문자열 연산 소스 코드: Lib/string.py 문자열 상수 이 모듈에 정의된 상수는 다음과 같습니다: string.ascii_letters 아래에 나오는 ascii_lowercase와 ascii_uppercase 상수를 이어붙인 것입니다

docs.python.org


이 정도면 string 모듈에 대해서는 다 설명한 거 같다. 다음 모듈 탐구 시간에 만나도록 하자!

sys모듈은 Python 인터프리터의 환경설정 등에 접근한다. 흔히 사용하는 sys.modules 나 sys.path 뿐만이 아니라 더욱 폭넓은 기능과 정보들을 가지고 있으니, 이번 시간을 통해서 자세히 알아보도록 하자


sys에서 제공하는 정보

1. sys.argv : Python 스크립트에 전달된 명령 줄의 인자를 LIST형태로 전달한다. 스크립트 이름은 sys.argv[0]를 가지고 뒤에 전달이 되면 순서대로 sys.argv [1], [2]가 된다. 만약 실행형 옵션인 -c를 설정하면 sys.argv [0]는 "-c"가 된다.

2. sys.byteorder : 현재의 바이트표기법이 little인지 big인지를 표시한다.

3. sys.builtin_module_names : 현재 Python 인터프리터로 가져온 모든 모듈의 이름을 제공하는 문자열을 튜플로 반환한다.

4. sys.copyright : Python Interpreter의 저작권을 문자열로 반환한다.

5. sys.dont_write_bytecode : 이게 True면 모듈 import 시. pyc를 만들지 않는다.

6. sys.pycache_prefix : 이게 설정되면(디렉터리 문자열로 설정)  __pycache__에 pyc가 생성되는 것이 아니라, 같은 디렉터리 문자열에 pyc가 생성이 된다.

7. sys.float_info : 실수는 운영환경에 따라 표현의 깊이 달라진다. 그 표현 깊이를 반환한다.

8. sys.hash_info : 해시 알고리즘을 계산하는 머신의 정보를 튜플로 반환한다.

9. sys.int_info : 정수 표현의 수준을 튜플로 반환한다.

10. sys.maxunicode : 가장 큰 유니코드의 코드 값을 반환한다.

11. sys.modules : 이미 로드된 모듈을 저장한 Dictionary이다. API에서는 modules Dictionary를 직접 바꾸는 것은 추천하지 않는다.

12. sys.path : 모듈이 탐색될 수 있는 경로를 가진 LIST이다. sys.path에 경로가 추가되면, 그 위치에서 모듈을 절대 경로를 생략하고 import 할 수 있다.

13. sys.platform : Python이 실행되는 플랫폼을 문자열로 반환한다.

14. sys.thread_info : 스레드 구현에 관련된 정보를 담은 Tuple

15. sys.version : Python 인터프리터의 버전 번호와 빌드 번호 등을 포함한 문자열, 유사한 기능으로 sys.api_version, sys.version_info, sys.winver

 

 

sys에서 제공하는 함수

1. sys.exit([args]) : SystemExit이라는 Exception을 발생시켜 Python에서 빠져나온다. 따라서 이를 try - except으로 예외 처리하면 Python은 종료되지 않는다.

2. sys.getallocatedblocks() : 현재 Pythond이 메모리에서 차지한 공간을 출력한다. 메모리 Leak 등을 검사하는 데 사용된다.

3. sys.getdefaultencoding() : 현재 기본 문자열 인코딩 타입을 반환한다.

4. sys.getrecursionlimit() : 스택의 재귀 깊이를 반환한다. setrecursionlimit(int)로 설정할 수도 있다.

5. sys.getsizeof(object) : 객체의 크기를 바이트 단위로 반환한다. 객체가 참조하는 다른 객체의 크기를 포함하지 느 않는다.

6. sys.intern(String) : String을 메모리 사전에 등록한다. 이는 성능 최적화를 위한 것으로, 만약 'ABC"라는 문자열이 intern으로 등록되면, a="ABC', b="ABC"라고할 때 a와 b는 같은 메모리 위치를 가진다.

7. sys.is_finalizing() : Python이 종료 중이면 True, 아니면 False를 반환한다.

 

 

표준 스트림

1. sys.stdin : 표준 입력 스트림(객체다)

2. sys.stdout : 표준 출력 스트림(객체다)

3. sys.stderr : 표준 에러 스트림(객체다)

 

* 출처 : docs.python.org/ko/3/library/sys.html

 

sys — 시스템 특정 파라미터와 함수 — Python 3.9.4 문서

sys — 시스템 특정 파라미터와 함수 이 모듈은 인터프리터에 의해 사용되거나 유지되는 일부 변수와 인터프리터와 강하게 상호 작용하는 함수에 대한 액세스를 제공합니다. 항상 사용 가능합니

docs.python.org


이번 시간에는 Python인터프리터와 실행환경에 관련된 sys에 관하여 조금 깊게 알아보았다. 위에 기술된 정보나 함수 외에도 저수준의 제공 기능(함수의 breakpoint, hook, audit 등)을 또한 제공하니, API를 읽어보는 것을 추천한다.

컴퓨터 세계에서 자주 사용되는 json이라는 표준이다. json은 Python을 자주 사용하는 사람이라면 아는 dictionary이라는 자료구조와 유사하게 생겼는데, 광범위하게 데이터 오브젝트를 인간이 읽을 수 있는 상태에서 전달하기 위한 표준이다. 다음은 json의 예시이다.

{
    "file_format_version" : "1.0.0",
    "ICD": {
        "library_path": ".\\XXX.dll",
        "api_version": "1.0.3"
    }
}

// json파일의 예시



json처럼 객체를 저장하고 나중에 재구성할 수 있는 포맷으로 표기하는것을 직렬화(Seriallization), 그 데이터를 다시 불러오는 것을 역직렬화(Deseriallization)이라고 한다. Python은 json처럼 인간 읽을 수 있는 방식 말고 pickle이라는 모듈을 이용해서 바이트 수준의 직렬화를 제공한다.



pickle을 이용한 데이터의 저장


다음은 students LIST객체를 my_list.dump에 저장하는 예시이다. byte연산을 수행하는 만큼 파일모드를 wb로 열었다.

>>> import pickle
>>> students = ["CHEEL","GLAADOS","STEENLY"]
>>> with open("my_list.dump","wb") as f:
	pickle.dump(students,f)

>>> b_students = pickle.dumps(students)
>>> print(b_students)
b'\x80\x04\x95!\x00\x00\x00\x00\x00\x00\x00]\x94(\x8c\x05CHEEL\x94\x8c\x07GLAADOS\x94\x8c\x07STEENLY\x94e.'


* b_students처럼 저장하는 대신 pickle.dumps(students)로 바이트 반환받을 수 있다.

 



pickle을 이용한 데이터의 불러오기


다음은 my_list.dump에 저장된 바이트스트림을 students에 다시 할당하는 코드이다. 역시 rb로 열었다.

>>> import pickle
>>> with open("my_list.dump","rb") as f:
	new_students = pickle.load(f)

>>> print(new_students)
['CHEEL', 'GLAADOS', 'STEENLY']
>>> new_b_students = pickle.loads(b_students)
>>> print(new_b_students)
['CHEEL', 'GLAADOS', 'STEENLY']


* 역시 pickle.loads(datas)로 파일연산없이 직접 받을 수 있다.



* pickle불가한 객체를 pickle 하면 PicklingError라는 Exception이 발생한다. 가능한 객체 목록은 API를 참조하자
* 클래스 인스턴스를 pickle하는 방법이 API에 나와있다. __init__()이 호출되는 것이 아닌, 기존의 attribute를 저장했다가 load 하는 방식인데, 사용자가 재정의할 수 있다는 점에서 재미있는 이야기이니, API를 한 번씩 읽어보기를 바란다.
* 과거에는 pickle과 비슷한 marshal이라는 모듈이 있었다고한다.(안써봤다.) API에서는 여러 가지 이유로 marshal대신 pickle을 쓰기를 권장한다.

 

* 출처 : docs.python.org/ko/3/library/pickle.html

 

pickle — 파이썬 객체 직렬화 — Python 3.9.4 문서

pickle — 파이썬 객체 직렬화 소스 코드: Lib/pickle.py pickle 모듈은 파이썬 객체 구조의 직렬화와 역 직렬화를 위한 바이너리 프로토콜을 구현합니다. 《피클링(pickling)》은 파이썬 객체 계층 구조가

docs.python.org



이번시간에는 Python의 직렬화 / 역직렬화 방식인 pickle에 대해서 알아보았다. 거대한 데이터를 저장하거나, 변수의 프로그램 수명에서 재사용할 시 채용할 수 있는 기법이니, 익혀두면 도움이 될 것이다.

먼길 오느라 수고들 많으셨습니다. 이번 포스팅은 Python과 매우 친해지기 그 마지막 시간인 Decorator이다. 물론 여기까지로 Python의 100%를 안다고 생각하면 큰 오산이다. 다음 시간부터는 모듈 알아보기 포스팅을 올릴 예정이니, 계속해서 Python에 대한 애정을 멈추지 말아주었으면 한다.


Decorator

여러분이 만들고 있는 함수의 프로그램 동작시간을 측정한다고 가정을 하자. 간단하게 import datetime모듈의 현재시간을 출력하는 datetime.datetime.now() 함수를 이용해보자. 

## 코드 : 
import datetime

def add(val1, val2) :
    print(datetime.datetime.now())
    print(val1+val2)
    print(datetime.datetime.now())
    return val1+val2

def sub(val1, val2) :
    print(datetime.datetime.now())
	print(val1-val2)
    print(datetime.datetime.now())
    return val1-val2

def div(val1, val2) :
    print(datetime.datetime.now())
    print(val1/val2)
    print(datetime.datetime.now())
    return val1/val2

if __name__=="__main__" :
    print(add(3,5))
    print("\n")
    print(sub(3,5))
    print("\n")
    print(div(3,5))
    
## 출력 :

2021-04-10 21:40:10.219000
8
2021-04-10 21:40:10.271000
8


2021-04-10 21:40:10.309000
-2
2021-04-10 21:40:10.337000
-2


2021-04-10 21:40:10.373000
0
2021-04-10 21:40:10.400000
0

으흠; 모든 함수에 대해서 datetime.datetime.now()를 2번씩 처주는 동일한 일을 해줄 필요가 있을까? Decorator는 이러한 문제의 해결책이다. 다음의 예시를 보자

import datetime

def deco(func) :
    def decorated() :
        print(datetime.datetime.now())
        func()
        print(datetime.datetime.now())
    return decorated
    
@deco
def function() :
    print("Function start")

if __name__=="__main__" :
    function()

우선 함수 그 자체를 파라미터로 받아들이는 또다른 함수 deco를 정의하였다. deco의 내부에는 인라인 함수, decorated를 정의하여 func() 실행 앞뒤로 우리가 원하는 행위인 print(datetime.datetime.now())를 실행해주었고, deco함수는 decorated 함수를 리턴하는 것으로 정의부는 마무리된다. 

 

decorated 받을 함수인 functiond은 앞부분에 @deco를 기술하여 이 함수가 deco함수로 decorated 될 것을 명시해 주었다.

 

* Decorator에서 인자를 사용하려면, @deco("some")으로 사용하면된다. 


자 이렇게 Python과 매우 친해지기 포스팅이 마무리되었다. 필자도 수고했고, 여러분도 수고 많았다. 처음에 Python을 그림에 비유했는데, 이는 아직도 유효하다. 여러분들은 그림의 기초적인 테크닉을 배운 것이고, 이제 여러 가지 프로그램을 만들 때 이 정보들이 여러분에게 큰 도움이 될 것이라고 확신한다. 

이제 매우 친해지기의 강의도 얼마 남지 않았다.(아마 decorator와 lambda까지 하면 여러분이 Python과 충분히 친해진 것을 느낄 것이다.) 이번 시간에는 Python이 제공하는 깔끔하고 멋있는 기능인 내포에 대해서 알아보자


LIST Comprehension

일단 내포가 무엇인지를 알아야한다. 내포는 자료구조 안에 for와 if 등을 활용하여 코드를 직관적이고 단순하게 하고, 실행 속도의 향상도 도모하는 All stat 아이템 같은 존재이다.

다음 코드는 0부터 9까지 짝수만을 선택하여 my_list로 만드는 코드이다.

>>> my_list = [x for x in range(10) if x%2==0]
>>> my_list
[0, 2, 4, 6, 8]

 

다음과 같이 for를 중첩하여 생성할 수도 있다.

>>> my_list = [x*y for x in range(5) for y in range(5)]
>>> my_list
[0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 2, 4, 6, 8, 0, 3, 6, 9, 12, 0, 4, 8, 12, 16]

작살난다.

 

자주쓰는 예시로 다음과 같이 현재 폴더의. txt확장자만을 가져오는 경우 다음과 같은 내포를 사용할 수 있다.

>>> import os
>>> cur_txt = [x for x in os.listdir() if x.endswith(".txt")]

 

Dict Comprehension

Dictionary또한 Python 3 버전 이상은 내포를 지원하게 되었다. 다음은 0~9까지의 수를 key로 가지고, 그 세제 곱수를 value로 가지는 my_dict의 내포 선언이다.

>>> my_dict = {x:x**3 for x in range(10)}
>>> my_dict
{0: 0, 1: 1, 2: 8, 3: 27, 4: 64, 5: 125, 6: 216, 7: 343, 8: 512, 9: 729}

물론 dictionary의 내포도 중첩된 for문을 활용할 수 있지만, dictionary의 특성상 중첩된 key를 가질 수 없기 때문에 다음과 같이 사용하면 key인 x 당 마지막 루프의 y만이 적용된다.(물론 x, y를 이용해서 유일한 key를 만들면 해결되는 문제이다.

>>> my_dict = {x:x*y for x in range(10) for y in range(5)}
>>> my_dict
{0: 0, 1: 4, 2: 8, 3: 12, 4: 16, 5: 20, 6: 24, 7: 28, 8: 32, 9: 36}

 

이외에도 SET자료형도 비슷한 방식으로 내포 선언할 수 있으니, SET자료형을 선언할 기회가 생긴다면 써보도록 하자(아마 잘 안 올 것이다.


내포는 다른 프로그램 언어는 대부분 지원하지 않는 기능이다. 아마 다른 코드를 주로 사용하다가 이 내포 기능을 Python에서 처음 만난 사람이라면 Python에 매력에 더욱 반할 것이다. 다음 시간에는 익명 함수 Lambda에 대해서 알아보도록 하자

예약어 시간 for문의 정의를 본 사람은 Iterable이라는 말을 보았을 것이다.(안 썼나?) 이번 시간에는 Iterator에 대해서 알아보면서 우리가 사용하는 반복문을 조금 더 높은 수준으로 올려보자


Iterable 한 것과 Iterator

Iterable 하다는 것은 문자 그대로 반복이 가능하다는 의미를 가진다. 즉 다음과 같은 객체들은 for, enumerate에서 반복할 수 있다.

Iterable 한 객체 : list, dict, set, tuple, range, str 등등

Iterable 한 객체들은 내부적으로 __iter__() 매소드를 가지고 있다. 이를 이용하여 아래와 같은 Iterator 객체를 생성할 수 있다.

>> my_list = [1,2,3,4,5,6,7,8,9,10]
>> my_iter = iter(my_list)

또한 Iterator로 생성된 객체는 next매소드를 가지고 있어, next를 이용하여 원소들을 탐색할 수 있다.

>>> my_list = [1,2,3,4,5,6,7,8,9,10]
>>> my_iter = iter(my_list)
>>> next(my_iter)
1
>>> next(my_iter)
2
>>> next(my_iter)
3
>>> next(my_iter)
4

## my_iter의 끝으로 가면 next호출 시 Exception이 발생한다.

 

Advanced For

향상된 for문은 다른 프로그래밍언어에서도 심심치 않게 볼 수 있는 것으로 Iterable 한 객체들의 원소를 반복자가 순회하는 것이다. 다음의 예시를 보자

## 코드 :
fruits = ["apple","orange","strawberry","kiwi"]

for i in fruits :
    print(i)

## 출력 :
apple
orange
strawberry
kiwi

한 가지 재미있는 점은 반복자가 인덱스였을 때(range()를 이용한 순회)는 원소의 수정이 가능했지만, A-for문을 통한 반복에서는 원소의 직접 수정이 안된다는 점이다.

## 코드 :
fruits = ["apple","orange","strawberry","kiwi"]

for i in fruits :
    i="ruin"

print(fruits)

for i in range(len(fruits)) :
    fruits[i] = "ruin"

print(fruits)

## 출력 :
['apple', 'orange', 'strawberry', 'kiwi']
['ruin', 'ruin', 'ruin', 'ruin']

 

Generator

이러한 Iterator는 지정된 자료구조말고도 Generator를 이용하여 사용자가 정의할 수 있다. 다음의 예시를 보자 

## 코드 : 
def my_iterator() :
    my_list = [1,2,3,4,5,6,7,8,9,10]
    for i in range(len(my_list)) :
        yield my_list[i]

a = my_iterator()
next(a)
next(a)
next(a)

## 출력 :
1
2
3

이렇게 생성된 generator는 A-for문에 사용할 수 있다.

## 코드 : 
def my_iterator() :
    my_list = [1,2,3,4,5,6,7,8,9,10]
    for i in range(len(my_list)) :
        yield my_list[i]

a = my_iterator()

for i in a :
    print(i)


## 출력 :
1
2
3
4
5
6
7
8
9
10

 

next를 호출하거나, 반복을할때 generator는 다음 yield를 호출하기 전까지 정지한 것과 같다. 다음의 예시를 보자

## 코드 : 
def my_iterator() :
    my_list = [1,2,3,4,5,6,7,8,9,10]
    for i in range(len(my_list)) :
        yield my_list[i]

a = my_iterator()

next(a)
next(a)

## 출력 :
currently were 0
currently were 1

 

Dictionary의 반복

이걸 배우기 까지 너무나도 많은 길을 걸어왔다. 우리들이 Advanced-For를 배울때까지 기다린 Dictionary에게 경의를 표하자 아래의 코드로 확인하자

## 코드 :
students = {"P_01":"CHEEL","O_02":"GLAADOS","R_03":"STEENLY"}

for index, value in students.items() :
    print(index, value)

## 출력 :
P_01 CHEEL
O_02 GLAADOS
R_03 STEENLY

students.items()는 우선 iterator는 아니다. 이는 dictionary의 특징을 생각하면 잘 알수 있는데, dictionary는 순서가 없다는것이다. 따라서 a = students.items()를 했을때 a는 dict_item이라는 객체로 출력됨을 알 수 있다.

 


이번 시간에는 iterable 한 객체와 나만의 Iterator를 생성하는 법, 늦게나마 Dictionary를 반복하는 방법과 Advanced for를 사용하는 방법을 배웠다. 이제 Python과 짱친먹을 시간도 얼마 남지 않았다. 다음 시간에는 내포(Comprehension)에 대해서 알아보도록 하자

+ Recent posts