파이썬에서 숫자를 출력하고자 하는데, 앞에 0을 붙여주고 싶을 때 해결 방법을 찾아보았습니다.

이때 사용할 수 있는 함수가 zfill()이라는 함수와 rjust()라는 함수가 있는데, 함수를 다음과 같이 사용할 수 있습니다.


01. zfill(width) 함수 사용

#"002"
"2".zfill(3)

#"50000"
"50000".zfill(5)

#"00123"
"123".zfill(5)




02. rjust(width, [fillchar]) 함수 사용

#"002"
"2".rjust(3, "0")

#"50000"
"50000".rjust(5, "0")

#"00123"
"123".rjust(5, "0")

#"aa123"
"123".rjust(5, "a")

rjust 함수는 원하는 문자를 따로 지정해줄 수 있기 때문에 "a"와 같이 다른 문자열로 앞 부분을 채워줄 수 있는 특징이 있습니다.









'PYTHON > Python 문법' 카테고리의 다른 글

Python Iterator란?? Python Iterable이란??  (0) 2017.01.27
Python Yield란?? What is the Yield??  (3) 2017.01.26
Python2.X Python3.X 함께 쓰기  (0) 2017.01.02
Python으로 파일 다운로드  (5) 2015.08.07

Iterator(반복자)를 먼저 알아보기 전에 Iterable이 무엇인지 알아야 하겠죠?

먼저 Python doc의 Iterable 정의를 보도록 합시다.



 Iterable


  An object capable of returning its members one at a time. Examples of iterables include all sequence types (such as liststr, and tuple) and some non-sequence types like dict and file and objects of any classes you define with an __iter__() or __getitem__() method. Iterables can be used in a for loop and in many other places where a sequence is needed (zip()map(), ...). When an iterable object is passed as an argument to the built-in function iter(), it returns an iterator for the object. This iterator is good for one pass over the set of values. When using iterables, it is usually not necessary to call iter() or deal with iterator objects yourself. The for statement does that automatically for you, creating a temporary unnamed variable to hold the iterator for the duration of the loop. See also iterator, sequence, and generator.



iterable을 정리해보면 member를 하나씩 반환할 수 있는 object를 말합니다.

iterable의 예로는 sequence type인 list, str, tuple이 있습니다. 


데이터 타입에 상관 없이 순회를 구현하고자 하는 필요성은 파이썬에만 있었던 것은 아니었다고 합니다. Iterator Pattern은 이미 하나의 디자인 패턴으로 정형화 되어 있는 것 중 하나입니다. 데이터 타입의 내부 구현을 노출하지 않고 포함하고 있는 요소들을 순회할 수 있는 방법을 제공하는 패턴을 이터레이터 패턴이라고 합니다.

※ 대부분 언어에서 이터레이터 패턴을 콜렉션 라이브러리의 구현 방식이나 문법 요소로 제공하고 있기는 하지만 파이썬 만큼 언어 자체에 자연스럽게 녹아 있는 경우는 많지 않다록 합니다.


사실 단순한 for문의 range도 iterable할 수 있는 list가 생성되었기 때문에 차례대로 사용할 수 있었던 것입니다.

for i in range(0,3):
	print(i)
#0
#1
#2

즉, range로 생성된 list가 iterable 할 수 있기 때문에 차례대로 member를 불러와 쓸 수 있다는 것입니다.

그리고 dictionary의 type인 dict도 iterable 할 수 있습니다. 그래서 다음과 같은 결과를 가지고 옵니다.

x = ['a':1, 'b':2, 'c':3]

for i in x:
	print(i)
	
#a
#b
#c

그리고 __iter__()나 __getitem__() 메소드로 정의된 class는 모두 iterable 할 수 있다고 합니다. 

iterable은 for loop말고도 zip(), map()과 같이 sequence 한 특징을 필요로 하는 작업을 하는 데 유용합니다.



zip([iterable, ...])

  This function returns a list of tuples, where the i-th tuple contains the i-th element from each of the argument sequences or iterables.


map(function, iterable, ...)

  Apply function to every item of iterable and return a list of the results.



zip()과 map()을 보면 argument를 iterable로 받는 것을 알 수 있습니다.


그리고 이제 iterator를 보도록 합시다.

iterator를 보기 앞서 먼저 Python docs의 정의를 살펴보도록 하지요.



Iterator


  An object representing a stream of data. Repeated calls to the iterator’s next() method return successive items in the stream. When no more data are available a StopIteration exception is raised instead. At this point, the iterator object is exhausted and any further calls to its next() method just raise StopIteration again. Iterators are required to have an __iter__() method that returns the iterator object itself so every iterator is also iterable and may be used in most places where other iterables are accepted. One notable exception is code which attempts multiple iteration passes. A container object (such as a list) produces a fresh new iterator each time you pass it to the iter() function or use it in a for loop. Attempting this with an iterator will just return the same exhausted iterator object used in the previous iteration pass, making it appear like an empty container.


 

iterator는 next()함수로 데이터를 순차적으로 호출 가능한 object입니다. 만약 마지막 데이터를 불러오고도 next()를 하거나 next()함수로 데이터를 가져올 수 없는 경우는 StopIteration exception을 발생시킵니다.


그렇다면 Iterable한 object는 iterator일까요?

결론부터 말씀드리자면, iterable하다고 해서 무조건 iterator라고 할 수는 업습니다.


저는 처음 iterator를 공부할 때, list를 iter()함수를 통해 iterable하게 만들어서 object를 생성해보았었습니다.

list는 iterable이지만 next()함수로는 순차적으로 불러올 수 없었습니다. list로 선언된 것은 보통 a[0], a[1] ...등으로 불러오지요.

list는 순회 가능한(iterable) 오브젝트이지만 __getitem__() 함수를 IndexError가 발생할 때까지 0부터 차례로 호출하기 때문에 개념이 약간 다르다고 할 수 있습니다.



Iterator의 자세한 원리


iter() 함수는 들어온 컨테이너의 __iter__ 함수를 호출해주며, __iter__는 해당 컨테이너의 이터레이터를 반환(return)하도록 구현되어 있습니다. (이렇게 반환된 이터레이터는 __iter__ 함수와 next() -->(python3 에서는 __next__) 함수를 구현하고 있어야 함, 역으로 두 함수를 구현하고 있는 오브젝트를 이터레이터라고 함)
이터레이터의 __iter__와 next(혹은 __next__)는 내부적으로 사용하도록 의도된 함수이기 때문에 직접 부르기보다는 각각 내장 함수인 iter와 next를 이용하여 불러줍니다.


 만약 iterable하더라도 iterator가 아닙니다.

x = [1,2,3,4,5]
next(x)
Traceback (most recent call last):
	File .....
	
iterx = iter(x)
next(iterx)
#1


그리고 이제 list를 통해 예시를 설명하도록 하겠습니다.

아래의 소스를 보면 type이 어떻게 정의도는 지를 알 수 있습니다.

x = [1,2,3,4,5]
type(x)

#<class 'list'>

iterx = iter(x)
type(iterx)

#<class 'list_iterator'>


그리고 이제 python 3.x, python 2.7.x의 기준으로 next()를 사용하는 법을 보도록 하겠습니다.

x = [1,2]
type(x)

#<class 'list'>

iterx = iter(x)
type(iterx)

#<class 'list_iterator'>

#=-=-=-=-=-=-=-=-=-=-=- python 2 version
iterx.next()
#1
iterx.next()
#2
iterx.next()
#Traceback (most recent ...
#StopIteration
#=-=-=-=-=-=-=-=-=-=-=- python 3 version
next(iterx)
#1
next(iterx)
#2
#Traceback (most recent ...
#StopIteration


list는 1, 2로 이루어져 있으니, 3번째를 가져올 때 StopIteration을 하도록 되어 있습니다.

[그림 01]을 보면 더 확실하게 알 수 있겠습니다. ㅎㅅㅎ

[그림 01] python 2.7.x 와 python 3.x의 next()함수 사용법 비교


자, 그렇다면 next()함수를 통해 다음을 가져온다는 것은 알았는데, 살짝 더 들여다봅시다.

다음 소스는 4개의 list를 iterable한 list(list_iterator)를 구현하고 출력하는 프로그램입니다.

#iterator Test
#python 3 version source

List_first		= [1,2,3,4,5]
List_second 	= ['a','b','c','d']
List_third		= ["Third String", "Is", "List_Third~~"]
List_forth		= [10]

List_sum 		= []

List_sum.append(List_first)
List_sum.append(List_second)
List_sum.append(List_third)
List_sum.append(List_forth)


print("print List_sum : ", List_sum)

iterList = iter(List_sum)

print("\nmake it iterable!!!=-=-=-=-=-=-=-=-\n")

for i in range(4):
	print("print List_next()", next(iterList))



위의 소스를 실행시키면 [그림 02]와 같은 결과를 볼 수 있습니다.


[그림 02] list내의 list를 하나의 iterator로 생성


단순히 list를 가져온다는 것이 아니라 하나의 요소를 가져오는 것으로 이해하시면 더 좋을 것 같아 예시를 넣었습니다.

이러한 iteration은 [그림 03]과 같은 구조로 표현할 수 있습니다.


[그림 03] Iteration 구조



참고 URL : bluese05 님의 글

참고 URL : friberry 님의 글



Yield란 일종의 생성자(Generator)와 같습니다.

이더레이터(iterator)와는 비슷한 결과값을 가져오지만, 다른 것입니다.

이더레이터는 공부하다보니 알게 되었습니다. 일단 먼저 yield를 살펴보도록 하겠습니다.


먼저 Python docs에서 Generator의 정의를 보도록 합시다.



generator


  A function which returns an iterator. It looks like a normal function except that it contains yield statements for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function. Each yield temporarily suspends processing, remembering the location execution state (including local variables and pending try-statements). When the generator resumes, it picks-up where it left-off (in contrast to functions which start fresh on every invocation).



Generator는 Iterator를 생성해주는 함수라고 간단하게 설명할 수 있겠습니다. iterator는 next()함수를 통해 순차적으로 값을 가져오는 object를 말합니다.

(LINK : Iterator & Iterable에 대한 설명)


Generator는 일반함수릉 크게 다를 것은 없지만 yield라는 가장 큰 차이점을 가지고 있습니다. 

아래의 소스는 yield의 기본 예제입니다.

#python 3 version source
#yield generator test source
#yield_Basic_Test.py

def number_generator(n):
	print("Function Start")
	while n < 6:
		yield n
		n += 1
	print("Function End")
	
if __name__ == "__main__":
	for i in number_generator(0):
		print(i)
		


결과 값은 다음과 같습니다.

[그림 01] yield_Basic_Test.py 결과값


for문과 number_generator(n)함수의 합이 만나서 [그림 01]과 같은 결과값이 나옵니다.

number_generator()함수에는 return이 없는데 어떻게 저렇게 while문 내에서 돌아가는 n의 값이 출력이 되는 것일까요?

기존의 함수의 Call 모습과 yield가 포함된 함수의 Call 모습을 비교해보도록 하겠습니다.


[그림 02] Main에서 Normal_function으로의 함수 흐름


Main함수에서 Normal_function으로의 함수 흐름은 기존의 여타 함수의 흐름과 다를 게 없습니다.

Main을 실행하다가 Normal_function으로 가서 함수 내의 루틴을 실행시킨 후 Main으로 돌아가 남은 Main의 루틴이 실행됩니다.

그렇다면 이제 yield가 포함된 함수를 보도록 합시다.


[그림 03] Main에서 number_generator()으로의 함수 흐름


number_generator()으로의 함수 흐름이 일반 함수의 흐름과는 많이 다릅니다.

이 흐름은 yield라는 것 때문입니다. yield는 두 번째 흐름처럼 n의 값을 return 하고 다시 함수로 돌아가게끔해주는 것입니다.

그렇다면 다음 소스를 보겠습니다.


Python docs에서 정의된 yield의 정의를 보도록 합시다.



Yield


The yield statement is only used when defining a generator function, and is only used in the body of the generator function. Using a yield statement in a function definition is sufficient to cause that definition to create a generator function instead of a normal function.


When a generator function is called, it returns an iterator known as a generator iterator, or more commonly, a generator. The body of the generator function is executed by calling the generator’s next() method repeatedly until it raises an exception.


When a yield statement is executed, the state of the generator is frozen and the value of expression_list is returned to next()‘s caller. By “frozen” we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, and the internal evaluation stack: enough information is saved so that the next time next() is invoked, the function can proceed exactly as if the yield statement were just another external call.


As of Python version 2.5, the yield statement is now allowed in the try clause of a try ... finally construct. If the generator is not resumed before it is finalized (by reaching a zero reference count or by being garbage collected), the generator-iterator’s close() method will be called, allowing any pending finally clauses to execute.



yield는 generator가 일반 함수와 구분되는 가장 핵심적인 부분입니다. yield를 사용함으로써 어떤 차이가 있는지는 위의 일반 함수와 yield가 포함된 함수의 흐름으로 설명 드렸습니다.

더 자세하게 설명드리자면, generator 함수가 실행 중 yield를 만날 경우, 해당 함수는 그 상태로 정지되며, 반환 값을 next()를 호출한 쪽으로 전달하게 됩니다. 이후 해당 함수는 일반적인 경우처럼 종료되는 것이 아니라 그 상태로 유지되게 됩니다. 즉, 함수에서 사용된 local 변수나 instruction pointer 등과 같은 함수 내부에서 사용된 데이터들이 메모리에 그대로 유지되는 것입니다.   [bluese05님 글 출처]


#python 3 version source
#yield test source
#yield_Routine_Test.py

def generator_test(n):
	print("-=-=-=-=-=-= Generator Start =-=-=-=-=-=-")
	
	while(n < 3):
		print("<< Before Yield >>")
		yield n
		n += 1
		print("<< After Yield >>")
		
	print("-=-=-=-=-=-= Generator End =-=-=-=-=-=-")
	
if __name__ == "__main__":
	print("---------- Main Function Start ----------")
	
	for i in generator_test(0):
		print("Start For))))))))))))")
		print("Yield i is : ", i)
		print("End For))))))))))))))")
		
	print("---------- Main Function End ----------")
		

이 소스는 Main 함수와 generator_test()함수의 흐름을 보기 위한 함수입니다.

결과 값을 보도록 합시다.

[그림 04] yield_Routine_Test.py


결과값을 보면 먼저 Main이 시작하고 끝은 Main으로 끝납니다.

그리고 Generator 함수의 시작과 끝도 양 끝에 있습니다.

그렇다면 루틴은 <Before yield> -> for문 시작 -> for문 내의 print 실행 -> for문 끝 -> <After yield>로 되어 있고 이렇게 3회 반복됩니다.

이 루틴을 그림으로 그려보면 [그림 05]와 같습니다.


[그림 05] Main에서 for문으로 된 generator 함수의 흐름


먼저 Main이 실행되면서 for문 내의 generator_test함수로 들어갑니다. 그리고 함수가 시작되면서 while문으로 들어가 쭈욱 실행하면서 yield에서 n이라는 변수에 저장된 값을 반환합니다. 그리고, for문 내의 루틴이 실행된 후(print함수를 실행), 다시 yield 함수의 아래의 루틴을 실행 후 다시 while문이 실행되면서 루틴이 반복됩니다.

설명을 겁나 어렵게 했네요. 가볍게 <Before yield> -> for문 시작 -> for문 내의 print 실행 -> for문 끝 -> <After yield>로 루틴이 이루어져 있고, 그 겉에 Main과 generator함수가 있다고 보시면 됩니다.


자 이제, Python docs에서 Generator expression을 살펴보도록 하겠습니다.



generator expression


  An expression that returns an iterator. It looks like a normal expression followed by a for expression defining a loop variable, range, and an optional if expression. The combined expression generates values for an enclosing function:



generator expression이란 generator 함수를 좀 더 쉽게 사용할 수 있도록 제공되는 표현형식입니다. list comprehension과 비슷하지만, [] 대신 ()를 사용하면 됩니다. 아래의 소스는 0 ~ 9 사이 정수 중 홀수를 list와 generator object로 생성한 예제입니다.

[i for i in range(10) if i % 2]
#[1,3,5,7,9]
(i for i in range(10) if i % 2)
#<generator object at 0x......>


활용해보면 다음과 같은 소스와 결과를 볼 수 있습니다.

#python 3 version source
#Yield_Test.py

def generator(wordList):
	wordListLength = 0
	print("Generator Start!!")
	while(wordListLength < len(wordList)):
		yield wordList[wordListLength]
		wordListLength += 1
		
	
if __name__ == "__main__":
	Main_wordList = ["TEST", "IS", "WORK", "GOOD"]
	for i in generator(Main_wordList):
		print(i)
		
	print("Done!!!")


[그림 06] yield_Test.py 결과값





generator를 사용하는 이유는 크게 두 가지로 구분할 수 있습니다.


1. Memory를 효율적으로 사용할 수 있음

list comprehension과 generator expression으로 각각 생성했을 때 메모리 사용 상태를 봅시다.

#python 3 version 기준
import sys

a = [i for i in range(100) if i % 2] # list 
sys.getsizeof(a)
#268

b = [i for i in range(1000) if i % 2]
sys.getsizeof(b)
#2140

c = (i for i in range(100) if i % 2) # generator
sys.getsizeof(c)
#48

d = (i for i in range(1000) if i % 2)
sys.getsizeof(d)
#48


list의 경우 사이즈가 커질 수록 그만큼 메모리 사용량이 늘어납니다. 하지만, generator의 경우는 사이즈가 커진다해도 차지하는 메모리 사이즈는 동일한 것을 볼 수 있습니다. list와 generator의 동작 방식의 차이가 있을 뿐입니다.


list는 list 안에 속한 모든 데이터를 메모리에 적재하기 때문에 list의 크기 만큼 차지하는 메모리 사이즈가 늘어나게 됩니다. 하지만 generator의 경우 데이터 값을 한 번에 메모리에 적재하는 것이 아니라 next() 함수를 통해 차례로 값에 접근할 때마다 메모리에 적재하는 방식입니다.  [bluese05님 글 출처]


2. Lazy evaluation 즉 계산 결과 값이 필요할 때까지 계산을 늦추는 효과가 있음

예제로써 설명하도록 하겠습니다.

먼저 함수는 sleep_function(x)라고 선언하도록 합시다.

def sleep_function(x):
	print("Sleep..")
	time.sleep(1)
	return x


sleep_function()함수는 1초간 sleep을 수행한 후 x값을 return하는 함수입니다. 만약 위 sleep_function()함수를 이용해 list와 generator를 생성하면 어떻게 동작될지를 다음의 예제로 설명하도록 하겠습니다.

import time

def sleep_function(x):
	print("sleep...")
	time.sleep(1)
	return x

# list 생성
list = [sleep_function(x) for x in range(5)]

# generator 생성
generator = (sleep_function(y) for y in range(5))

for i in list:
	print(i)
	
print("=-=-=-=-=-=-=-=-=-=-=-=-=-=")

for j in generator:
	print(j)


위의 소스를 실행시키면 list생성과 generator 생성을 통해 각각 다른 방법으로 결과값을 받아오는 것을 알 수 있습니다.

결과 값은 [그림 07]과 같습니다.


[그림 07] list와 generator의 실행 차이


list의 경우는 list comprehension을 수행할 때, list의 모든 값을 먼저 수행하기 때문에 sleep_function() 함수를 range()만큼 한 번에 수행하게 됩니다.

generator의 경우 생성 시 실제 값을 로딩하지 않고, for문이 수행될 때 하나씩 sleep_function()을 수행하며 값을 불러오게 됩니다. generator의 특징 중 하나는 "수행 시간이 긴 연산을 필요한 순간까지 늦출 수 있다는 점이 특징"이라고 할 수 있습니다.


간단하게 말하면, list는 실행되고 결과 값을 list에 반환되어 [0,1,2,3,4] 값을 가지고 있게 됩니다.

그러나 generator는 실행되지 않고 object로 선언이 되기 때문에 필요한 때마다 실행시킬 수 있게 된 것입니다.


[그림 08] list comprehension과 generator expression 차이



참고 URL : haerakai님의 글

참고 URL : bluese05님의 글



참고 URL


<Python 버전 기준>

Python 2.7.13

Python 3.6.0


파이썬 3은 C:\ 바로 및에 Python3이 설치가 되지 않는 것을 보고 어떻게 해야 할지 막막했는데 참고 출처를 보고 설정할 수 있었습니다.

먼저 Python 3을 설치하고 Python 2를 설치했기 때문에 제가 설치했던 방식대로 설명을 진행하겠습니다.


Python 3.6.0(포스트 기준 버전) 설치 -> Python 2.7.13(포스트 기준 버전)설치


[그림 01] Python 2, 3 설치 후 환경변수 모습


[그림 01]의 환경변수에는 Python27밖에 없는 것을 볼 수 있습니다.

Python 3을 설치할 때는 [환경변수에 추가하시겠습니까]와 같은 설정을 추가하라고 되어 있습니다.

그러면 환경변수에는 추가가 되었다는 말인데... 어디있는치 찾아보니 [그림 02]와 같이 사용자 변수에 존재했습니다.

[그림 02] 사용자 변수에 존재하는 Python3


사용자 변수에는 Python 3의 위치가 존재하는데, 위치는 다음과 같습니다.

%USERPROFILE%\AppData\Local\Programs\Python\Python36-32


아무런 설정 없이 python 2.7과 python 3을 구분하는 것은 cmd 명령으로 다음과 같이 구분되어 있습니다.


 python 2 실행 

 python 

 python 2 백그라운드 실행 

 pythonw

 python 3 실행

 py

 python 3 백그라운드 실행

 pyw


다음 명령을 실행하는 것은 C:\Windows에 다음과 같은 파일이 있기 때문입니다.

그렇다면 우리는 C:\Windows 아래에 있는 파일을 다음과 같이 수정해줄 수 있습니다.


python.exe => python2.exe

pythonw.exe => pythonw2.exe


py.exe => python3.exe

pyw.exe => pythonw3.exe


만약 다음과 같은 파일이 없다면 mklink로 파일을 생성해줄 수 있습니다.

mklink c:\Windows\python2.exe [파이썬 2폴더에 python.exe가 있는 위치]

mklink c:\Windows\python3.exe [파이썬 3폴더에 python.exe가 있는 위치]

[그림 03] python2, python3 명령 실행


저는 python2와 python3를 구분하기 위해서 가능하면 cmd로 실행합니다... 구분을 쉽게 하고 명확하게 하기 위합입니다. *^^*




참고 링크 : Python - The Mouse vs The Python


어제 'AnLab'의 보고서들을 읽어보기 위해 다운로드 받으려 하는데, 65개나 되는 파일을 다운로드 받으려고 하니 너무 번거로울 거 같아 Python으로 파일을 다운로드 받을 수 있지 않을까 하는 마음에 찾아보게 되었어요.


기본적 예제는 이렇게 나와요.

각각 urllib, urllib2를 이용하여 파일을 다운로드해요.

url : URL을 저장하는 String 변수의 역할을 해요.

urlretrieve(URL주소, 저장할 이름) : 다운로드할 파일이 있는 곳에서 URL주소에서 지정한

f = urllib2.urlopen(URL주소) : 지정한 URL을 열고, 그 지정한 곳을 f에 저장하는 것 같아요.

data = f.read() : data에 지정한 주소의 파일 데이터를 읽어드려 data에 binary로 저장하지요.

그 이하는 추후 추가 할래요... 지금은 바빠서.


제가 작성한 소스에요.

URL은 Ahnlab에서의 ASEC_Report목록입니다.

for문으로 0부터 53까지 하려 했는데 54까지 해븟네요.

필요한 건 Vol.13부터 Vol.65까지 다운받고 Vol.66은 URL이 없어서인지 다운로드는 되지 않았어요.


파이썬 참 대단한 것 같다고 새삼 다시 느끼네요.


PS URL이 없는 곳에서 파일을 다운로드 해보려고 하면 이런 결과가 나오네요.

No.65는 잘 다운로드된 파일.

No.66는 다운로드되지 않은 파일.


2KB밖에 되지 않네요.




+ Recent posts