base64라는 인코딩 방식을 들어본 적이 있는가? 컴퓨터 분야에서 상당히 많이 쓰이는 인코딩 방식이넫, 이진 파일들을 문자 코드에 영향을 받지 않는 64개의 ASCII를 사용하여 일련의 문자열로 변환하는 인코딩 방식이다. 이와 비슷하게 base32, base 16 방식도 존재하고, ASCII 문자 자체가 128개가 되지 않기 때문에 base128은 없는 방식이다.(대신 귀 여운 8 5를준 비했습니다.) 특수한 규약으로는 base64로 인코딩 된 문자는 = 혹은 ==으로 끝이난 다는 점이다. 모듈 탐구 시간, 오늘은 이 base인코딩을 다루는 base64 모듈을 알아보도록 하자


base64모듈에서 사용되는 함수들은 전부 base64 클래스의 클래스 함수이다.

 

부호기 - ENCODER

1. base64.b64encode(s) : 바이트 객체 s를 받아 인코딩 된 byte를 반환한다.

2. base64.standard_b64encode(s) : 바이트 객체 s를 받아 표준 base64알파벡으로 인코딩 된 byte를 반환한다.

3. base64.urlsafe_b64encode(s) : 바이트 객체 s를 받아 url에서 사용 가능한 base64인 코딩된 byte를 반환한다.

4. base64.b32encode(s) : 바이트 객체 s를 받아 base32로 인코딩 된 byte를 반환한다.

5. base64.b16encode(s) : 바이트 객체 s를 받아 base 16으로 인코딩 된 byte를 반환한다.

6. base64.a85encode(s) : 바이트 객체 s를 받아 Ascii85를 사용해 인코딩 된 byte를 반환한다.

7. base64.b85encode(s) : 바이트 객체 s를 받아 base85를 사용해 인코딩 된 byte를 반환한다.

8. base64.encode(input,output) : input, output은 file객체여야 한다. input의 내용을 base64로 인코딩하여 output에 쓴다.

9. base64.encodebytes(s) : 바이트객체 s를 받아 base64로 인코딩하고, 76바이트마다 개행(b"\n")을 포함하여 결과를 반환한다.

 

복호기 - DECODER

1. base64.b64decode(s) : 바이트 객체 s를 받아 인코딩 된 byte를 반환한다.

2. base64.standard_b64decode(s) : 바이트 객체 s를 받아 표준 base64알파벡으로 인코딩 된 byte를 반환한다.

3. base64.urlsafe_b64decode(s) : 바이트 객체 s를 받아 url에서 사용 가능한 base64인 코딩된 byte를 반환한다.

4. base64.b32decode(s) : 바이트 객체 s를 받아 base32로 인코딩 된 byte를 반환한다.

5. base64.b16decode(s) : 바이트 객체 s를 받아 base 16으로 인코딩 된 byte를 반환한다.

6. base64.a85decode(s) : 바이트 객체 s를 받아 Ascii85를 사용해 인코딩 된 byte를 반환한다.

7. base64.b85decode(s) : 바이트 객체 s를 받아 base85를 사용해 인코딩 된 byte를 반환한다.

8. base64.decode(input,output) : input, output은 file객체여야 한다. input의 내용을 base64로 인코딩하여 output에 쓴다.

9. base64.decodebytes(s) : 바이트객체 s를 받아 base64로 인코딩하고, 76바이트마다 개행(b"\n")을 포함하여 결과를 반환한다.

 

코드 예시

base64.encode, base64.decode를 제외한 모든 코드의 사용이 동일하여 일반 문자열 strs가 base64로 encode > decode 되는 순서의 예시를 첨부한다.

>>> import base64
>>> strs = "나의 문자열 썸띵입니다,"
>>> strs = strs.encode()
>>> strs
b'\xeb\x82\x98\xec\x9d\x98 \xeb\xac\xb8\xec\x9e\x90\xec\x97\xb4 \xec\x8d\xb8\xeb\x9d\xb5\xec\x9e\x85\xeb\x8b\x88\xeb\x8b\xa4,'
>>> en = base64.b64encode(strs)
>>> print(en)
b'64KY7J2YIOusuOyekOyXtCDsjbjrnbXsnoXri4jri6Qs'
>>> de = base64.b64decode(en)
>>> print(de)
b'\xeb\x82\x98\xec\x9d\x98 \xeb\xac\xb8\xec\x9e\x90\xec\x97\xb4 \xec\x8d\xb8\xeb\x9d\xb5\xec\x9e\x85\xeb\x8b\x88\xeb\x8b\xa4,'

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

 

base64 — Base16, Base32, Base64, Base85 데이터 인코딩 — Python 3.9.4 문서

base64 — Base16, Base32, Base64, Base85 데이터 인코딩 소스 코드: Lib/base64.py 이 모듈은 바이너리 데이터를 인쇄 가능한 ASCII 문자로 인코딩하고 이러한 인코딩을 다시 바이너리 데이터로 디코딩하는 함

docs.python.org


컴퓨터세상에 발을 들였으면, 인코딩 타입은 어느 분야에서든(DB, NETWORK, 웹 등등 진짜 어디서든) 사용된다. 그중 표준 인코딩 타입처럼 많이 사용되는 것이 base64이다. 이 모듈을 자유자재로 다룰 줄 알게 되면, 다른 분야에서 python을 사용하는 데에 많은 도움이 될 것이라고 확신한다.

2번째 모듈 탐구 이번 살펴볼 모듈은 random이다. 사실 random을 컴퓨터에서 완전히 구현하는 것은 불가하다고 본다. 일정수의 주기를 가지고 생성하는 유사 난수 생성기(PRING)로 난수를 생성하게 되는데, 이 난수를 더욱 잘 처리할 수 있게 도와주는 모듈이 random 모듈이다.


일단 random모듈에서 제공하는 함수는 Random 클래스에 정의된 클래스 메서드 이다.(random 모듈에서 제공하는 클래스는 random과 systemrandom 2가지이다.) 그중 대부분의 함수는 다음의 random.random()이라는 [0, 1.0) 사이 무작위 float를 반환하는 기본 함수에 의존한다.

 

난수 생성기를 관리하는 함수

random.seed(a=None) : 난수 생성기는 무엇을 기준으로 난수를 생성할지 기준(Seed값)을 가지게 된다. seed함수는 이 기준값을 초기화 하주며, None으로 정의될 경우 현재의 시스템 시간이 seed로 사용된다.

 

* 난수생성기의난수 생성기의 현재 상태는 getstate()로 구할 수 있고, setstate()로 설정할 수도 있다.(이를 이용하여 난수 생성기의 당시 상태를 저장, 불러올 수 있다.)

 

난수 생성기의 생성 함수

1. random.randbytes(n) : n자리의 무작의 byte를 생성한다/

2. random.randrange(start, stop, step) : range(start, stop, step)중 임의로 선택된 요소를 반환한다. 만약 파라미터가 1개가 주어지면 range(parameter)에서 무작위로 선택한다.

3. random.randint(a, b) : a <=N <=b에 부합하는 임의의 N을 반환한다.

4. random.getrandbits(k) : k bit로 이루어진 양의 정수를 반환한다.

5. random.random() : [0.0, 1.0)의 구간에서 임의의 부동 소수점 숫자를 반환한다.

6. random.uniform(a, b) : a <=N <=b에 부합하는 임의의 부동소수점 N을 반환한다.(b> a인 경우 반대로)

>>> import random
>>> random.randbytes(3)
b'\x81\xebA'
>>> random.randrange(0,10)
2
>>> random.randint(0,10)
7
>>> random.getrandbits(3)
7
>>> random.random()
0.3533718176197945
>>> random.uniform(3,5)
3.3321894552898152

 

난수 생성기의 시퀀스 처리 함수

1. random.choice(seq) : 비어있지 않은 seq에서 임의의 요소를 반환한다.

2. random.choices(population, k=n) : population에서 중복을 허락하며 길이가 k인 list를 반환한다.

3.. random.sample(population, k) : population에서 중복을 허락하지 않고 길이가 k인 list를 반환한다. 

4. random.shuffle(seq) : seq를 섞습니다.

>>> import random
>>> a=[1,2,3,4,5,6,7,8,9,10]
>>> random.choice(a)
7
>>> random.choices(a,k=5)
[2, 10, 5, 7, 5]
>>> random.sample(a,5)
[9, 6, 5, 8, 3]
>>> random.shuffle(a)
>>> a
[7, 8, 4, 1, 3, 2, 9, 5, 6, 10]

 

* Python은 메르센 트위스터(Mersenne Twister)라는 난수 생성기를 사용한다. 이는 53비트의 정밀도와 2**19937-1의 주기를 가지는데(총각 그게 좋은 거유~?) 혹시나 자신만의 난수 생성기를 사용할 사람은 random클래스를 상속받아서 쓰라고 API에 쓰여있다.

* random모듈의 실수 반환 랜덤은 수학적인 radom함수를 다수 가지고 있다. 필자는 수학을 못해서 넘어갔지만, 공학도 분들께서는 gauss, expovariate, gammavariate, weibullvariate(아니 영어도 무슨 뜻인지 모르겠다.)등을 보고 사용하도록 하자

 

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

 

random — 의사 난수 생성 — Python 3.9.4 문서

random — 의사 난수 생성 소스 코드: Lib/random.py 이 모듈은 다양한 분포에 대한 의사 난수 생성기를 구현합니다. 정수에 대해서는, 범위에서 균일한 선택이 있습니다. 시퀀스에 대해서는, 무작위

docs.python.org


'm번모시았알을 d. 간 o 다이는 nr보 a아듈'

다음 시간은 다른 모듈 탐구 포스팅으로 여러분들을 찾아뵙겠다.

모듈 탐구 그 첫 번째 시간 time 함수이다. 사실 필자는 time 모듈을 사용하면서 sleep함수나 strptime 등 쓰던 함수만 쓰는 일이 다수 있지만.... 이번 기회에 time 모듈을 조금 공부하면서 아는 지식을 정리하고자 한다.


일단 용어를 정리하고 가야한다.

* 에포크(epoch) : 시간이 시작되는 시점(태초에 만물이 있을 지어니...) 플랫폼에 따라 다른데 예를 들면 유닉스는 1970년 1월 1일 00:00:00(UTC)이다. time.gmtime(0)로 알아볼 수 있다.

* UTC : 협정 세계시로, 그리니치 표준시인 GMT로 알려져 있다.

* DST : 일광 절약 시간으로 일 년 중 일부 기간 시간대를 한 시간 조정하는(서머타임)이다. 

 

먼저 에포크 이후의 시간은 time.time()으로 구할 수 있다. time.time()은 에포크 이후의 초를 나타내는 시간을 부동 소수점 숫자로 표현(뒤에 ns를 붙여서 정수로 나노초 반환 가능)한다.

>>> import time
>>> time.time()
1618071662.5779314
>>> time.time_ns()
1618071676597228800

 

클래스 : struct_time

time 모듈에 정의되며 자주 사용되는 객체는 struct_time이라는 객체이다.

이 struct_time은 다음과 같은 어트리뷰트를 가진다.

1. tm_year : 예를 들면 1998, 연도를 가리킨다.

2. tm_mon : 1-12의 달을 가리킨다.

3. tm_mday : 1-31의 달에 있을 수 있는 일자를 가리킨다.

4. tm_hour : 0-23의 시간을 가리킨다.

5. tm_min : 0-59의 분을 가리킨다.

6. tm_sec : 0-61의 초를 가리킨다.(?? 왜 61초이지?)

7. tm_yday : 1-366의 연에 있을 수 있는 일을 가리킨다.(아마 윤달 계산한 듯)

8. tm_isdst : 0,1,-1의 값을 가진다. 일광 절약 시간제가 발효 중인 경우 1 아니면 0, 모르면 -1

9. tm_zone : 시간대 이름의 약어, 필수는 아니다.

10. tm_gmtoff : UTC에서 동쪽으로 초 단위 오프셋, 필수는 아니다.

 

struct_time을 반환하는 함수

1. time.gmtime() : 에포크 이후의 초단위 시간을 tm_isdst가 0인 UTC로 반환한다.

2. time.localtime() : gmtime()과 같으나, 현지시각으로 변환한다.

3. time.strptime(Stirng) : 문자열로 시간을 맡아서 이걸 struct_time으로 반환한다.

>>> import time
>>> time.gmtime()
time.struct_time(tm_year=2021, tm_mon=4, tm_mday=10, tm_hour=16, tm_min=22, tm_sec=27, tm_wday=5, tm_yday=100, tm_isdst=0)
>>> time.localtime()
time.struct_time(tm_year=2021, tm_mon=4, tm_mday=11, tm_hour=1, tm_min=22, tm_sec=31, tm_wday=6, tm_yday=101, tm_isdst=0)
>>> time.strptime("11 Apr 2021","%d %b %Y")
time.struct_time(tm_year=2021, tm_mon=4, tm_mday=11, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=6, tm_yday=101, tm_isdst=-1)

 

struct_time → 부동소수점 실수

1. time.mktime(t) : struct_time자료형의 t를 받아들여 초를 부동소수점으로 반환한다.

>>> import time
>>> cur=time.localtime()
>>> time.mktime(cur)
1618071881.0

 

혹은 이 struct_time을 이쁜 말로 변경할 수도 있다.

1. time.asctime(t) : struct_time자료형의 t를 받아들여 ex : 'Sat Apr 10 15:42:41 2021'로 표현한다.

* 유사 함수로 time.ctime()이 있는데, 에포크 이후 초로 표현된 시간을 현지 시간으로 보인다.

2. time.strftime(format, t) : struct_time자료형을 받아들여 아래와 같은 규약으로 문자열을 생성한다.

지시자 의미
%a, %A 약식 요일, 전체 요일
%b, %B 약식 월, 전체 월
%c 적절한 날짜와 시간
%d 월중 일을 십진수(01~31)로 표현
%H, %I 각각 24, 12시간을 십진수로
%J 연중 일(001~366)을 십진수로
%m, %M 월, 분을 십진수로
%P AM, PM에 해당하는 시간값
%S 초를 십진수로
%U 연중 주번호를 십진수로(0~53)
%w 요일을 십진수로(0[일요일] ~ 6)

 

time모듈을 이용한 프로세스 실행시간의 측정

1. time.process_time() : 프로세스의 실행시간을 부동소수점 실수로 반환한다.(time.sleep으로 스킵된 시간 제외)

2. time.monotonic() : 단조 증가 시간을 부동소수점 실수로 반환한다.

3. time.perf_counter() : 프로세스의 실행시간을 부동소수점 실수로 반환한다.(time.sleep으로 스킵된 시간 포함)

* 위 함수들은 환경마다 달라지기에, 2번 호출하여 비교하는 것이 올바르다.

* 위 함수들은 뒤에 _ns를 붙여 정수의 나노초를 반환할 수 있다, ex : time.process_time_ns()

 

프로그램의 정지

Python 멈춰! 를 구현할 수 있는 time.sleep() 함수이다. time.sleep()은 아래 코드와 같이 사용되며, 호출하는 스레드를 일시 중단하는 기능을 가지고 있다.

import time
print("Python 멈춰!")
time.sleep(3)
print("3초후, 넌 이미 출력되어있다.")

위 코드의 2번째 print는 3초 후에 출력된다.

 

 

 

* time모듈에는 이 외에도 스레드의 시간을 스레드 별로 측정하게 도와주는 clock_XX함수가 있다. 궁금한 사람들은 다음의 API를 읽어보기를 바란다.

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

 

time — 시간 액세스와 변환 — Python 3.9.4 문서

time — 시간 액세스와 변환 이 모듈은 다양한 시간 관련 함수를 제공합니다. 관련 기능에 대해서는, datetime과 calendar 모듈도 참조하십시오. 이 모듈을 항상 사용할 수 있지만, 모든 플랫폼에서 모

docs.python.org

 


python에서 프로그램 실행시간을 측정하고, 시간에 관련된 함수와 구조체를 제공하며, 프로그램의 정지까지 제공하는 time모듈에 대해서 알아보았다. "에에? 시간에 관련해서는 datetime이 더 세련된 모듈 아닌가요><??" 모르는 소리, datetime 또한 import time as _time으로 구현되며 시작한다. 이로써 프로그램에서 우리의 시간을 다루는 능력이 더욱 향상되었기를 바란다. 

먼길 오느라 수고들 많으셨습니다. 이번 포스팅은 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을 그림에 비유했는데, 이는 아직도 유효하다. 여러분들은 그림의 기초적인 테크닉을 배운 것이고, 이제 여러 가지 프로그램을 만들 때 이 정보들이 여러분에게 큰 도움이 될 것이라고 확신한다. 

lambda는 간단하게 함수를 생성할 수 있게 도와주는 예약어이다. 이렇게 생성된 함수를 람다 함수 또는 익명함수라고도 부른다. 람다로 생성을 한 함수들은 여러 가지 내장 함수들로 조금 더 정밀한 코드를 작성할 수 있는데, 자세한 것은 아래 사용 코드를 보고 이야기하자

 


Lambda의 정의와 사용

 

람다함수는 다음과 같이 생성, 사용한다. 

>>> my_foo = lambda x : x+3
>>> my_foo(3)
6
>>> my_foo(5)
8

일반 함수처럼 정의했다면 다음과 같은 코드였을 것이다.

def my_foo(value) :
    return value+3

물론 여러개의 인자를 받을 수도 있다.

>>> my_foo = lambda x,y,z : x*y*z
>>> my_foo(1,2,3)
6

사용법이 어렵게 느껴지지 않기를 바란다. (그래야 아래가 쉽다)

 

 

map()

map은 함수와, iterable 한 객체를 인자로 받아들인다. 아래의 코드를 예시로 보자

>>> my_list = list(map(lambda x : x+3, range(5))
>>> my_list
[3, 4, 5, 6, 7]

* range 객체는 iterable한 객체로, 낮은 용량으로 list를 구현할 수 있게 해 준다. 완전한 list는 아니고, range라는 객체로 생성된다.

 

 

filter()

filter또한 함수와, iterable 한 객체를 인자로 받아들이는데, 함수의 결과가 True인 것만을 원소로 추가한다. 아래의 코드를 예시로 보자

>>> my_list = list(filter(lambda x: x%2==0, range(10)))
>>> my_list
[0, 2, 4, 6, 8]

사실 람다와 친구인 reduce라는 함수도 있다. functools의 하위 함수인데, 모듈을 추가하여 설명하는 건 되도록 나중으로 미루고 싶어서 설명을 거두었다. 다음 시간은 Python과 친해지기 그 마지막 편인 Decorator를 만나보도록 하자

이제 매우 친해지기의 강의도 얼마 남지 않았다.(아마 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에 대해서 알아보도록 하자

+ Recent posts