'Programming'에 해당되는 글 341건

  1. 2013.05.12 Greedy Quantifier & Lazy Quantifier
  2. 2013.05.12 구간 지정
  3. 2013.05.12 반복 찾기(+, *) 와 '?' 메타 문자
  4. 2013.05.12 포직스(POSIX) 문자 클래스
  5. 2013.05.12 Multithread Process
  6. 2013.05.09 발생자(Generator)
  7. 2013.05.09 반복자(Iterator)
  8. 2013.05.08 class 메소드 관련


탐욕적 수량자(Greedy Quantifier) & 게으른 수량자(Lazy Quantifier)


과하게 일치하는 상황 방지하기

RegEx: /<[Bb]>.*<\/[Bb]>

원하는 텍스트를 포함하긴 하지만, 찾으려 하지 않은 텍스트도 포함됐다. 그 이유는, 바로 별표(*)와 더하기(+) 같은 메타 문자가 탐욕적(greedy)이기 때문이다.

이는 가능한 한 가장 큰 덩어리를 찾으려 한다는 뜻이다. 이런 메타 문자는 찾으려는 텍스트를 앞에서부터 찾는 게 아니라, 텍스트 마지막에서 시작해서 거꾸로 찾는다. 의도적으로 수량자(quantifier)를 탐욕적으로 설계했기 때문이다.


하지만 만약 탐욕적 일치를 원하지 않는다면 어떻게 해야 할까? 

탐욕적 수량자를 게으른(lazy) 수량자로 바꾸면 된다. '게으른' 이라고 부르는 이유는 문자가 최소로 일치하기 때문이다. 게으른 수량자는 가존 수량자 뒤에 물음표(?) 를 붙여서 표현한다.

탐욕적 수량자에는 모두 각각 대응되는 게으른 수량자가 있다.


탐욕적 수량자와 게으른 수량자

 탐욕적 수량자

게으른 수량자 

 * 

 *? 

 + 

 +? 

 {n,} 

 {n,}? 



게으른 수량자를 이용

RegEx: /<[Bb]>.*?<\/[Bb]>

게으른 수량자인 *? 를 사용해 먼저 AK만 일치시켰고, 뒤이어 나머지도 찾아 두 부분을 따로 일치시켰다.



'Programming > RegEx' 카테고리의 다른 글

구간 지정  (0) 2013.05.12
반복 찾기(+, *) 와 '?' 메타 문자  (0) 2013.05.12
포직스(POSIX) 문자 클래스  (0) 2013.05.12
메타 문자 사용  (0) 2013.05.06
문자 집합으로 찾기  (0) 2013.05.06
Posted by scii
:

구간 지정

Programming/RegEx 2013. 5. 12. 21:46 |


구간 지정하기

더하기, 별표, 물음표는 정규 표현식을 쓰면서 발생하는 많은 문제를 해결해 주지만, 충분하지 않을 때도 있다. 다음과 같은 상황을 생각해 보자.

# 더하기와 별표는 일치하는 문자 수에 제한이 없다. 문자가 최대 몇 개까지 일치하는지 정할 수 없다.

# 더하기, 별표, 물음표가 일치하는 문자 수의 최솟값을 0이나 1이다. 일치하는 문자 수의 최솟값을 명시적으로 정의할 수 없다.

# 정확히 원하는 만큼만 일치하도록 문자 수를 정의할 수 없다.


이런 문제들을 해결하고, 연속하는 문자를 찾을 때 검색 조건을 더 구체적으로 지정하고자 정규 표현식에서는 구간을 사용한다.

구간은 중괄호([]) 안에 표시한다.



정확한 구간 찾기

문자가 일치하는 수를 정확히 정하려면 여는 중괄호와 닫는 중괄호 사이에 숫자를 넣는다. 즉 {3} 은 바로 앞에 있는 문자나 문자 집합이 세 번 연속해서 일치하는지 확인한다. 만약 요소가 두 개만 있다면, 패턴이 일치하지 않는다.

RegEx: 

16진수 여섯개가 일치하는 것을 검색



범위 구간 찾기

일치시키려는 요소 수의 최솟값과 최댓값을 나타낼 때도 구간을 사용한다. 범위는  {2,4} 처럼 표현한다. 최소 두 번에서 초대 네 번까지 일치시킨다는 의미이다.

RegEx: 

이 예제에서 쓴 정규식은 슬래시(/) 문자를 \/ 로 이스케이프 하였다. 많은 정규식 구현에서는 이 작업이 필요 없지만, 어떤 정규 표현식 해석기에는 필요하다. 

따라서 상 슬래시(/) 를 이스케이프 하는 편이 좋다.


※ 구간은 0부터 시작하기도 한다. {0,3} 은 요소가 없는 경우나 요소가 한 번 또는 두 번이나 세 번 일치함을 의미한다.

물음표(?)는 물음표 앞에 주어진 요소가 없는 경우나 요소 한개와 일치한다. 즉, 물음표(?) 는 {0,1} 과 같은 기능을 한다.



최소 구간 찾기

예를 들어 {3,} 은 최소한 요소가 세 번 일치함을 의미한다. 

RegEx: 


※ 더하기(+) 는 {1,} 과 기능이 같다.


'Programming > RegEx' 카테고리의 다른 글

Greedy Quantifier & Lazy Quantifier  (0) 2013.05.12
반복 찾기(+, *) 와 '?' 메타 문자  (0) 2013.05.12
포직스(POSIX) 문자 클래스  (0) 2013.05.12
메타 문자 사용  (0) 2013.05.06
문자 집합으로 찾기  (0) 2013.05.06
Posted by scii
:


문자 하나 이상 찾기

문자나 집합에 속한 요소를 하나 이상 찾으려면 간단히 문자 뒤에 더하기(+) 문자를 붙이면 된다. 더하기(+)는 문자가 하나 이상일 때 일치한다. (최소한 하나와 알치하고, 없을 때는 일치하지 않는다)

a가 a를 찾는 데 반해, a+는 하나 이상 연속된 a를 찾는다. 비슷하게 [0-9] 는 자릿수가 하나인 숫자를 찾는 데 반해, [0-9]+ 는 한 자리 이상 연속된 숫자를 찾는다.


TIP

문자 집합에 더하기(+)를 사용할 때는, 더하기(+)를 집합 바깥에 두어야 한다. 즉 [0-9+] 가 아니라 [0-9]+ 가 맞는 표현이다.

[0-9+] 가 틀린 정규 표현식은 아니지만, 하나 이상의 숫자와 일치하지는 않는다. 더 정학히 말하면 숫자 0부터 9와 더하기(+)로 집합을 정의한 것이고, 따라서 아무 한 자리 숫자나 더하기 기호와 일치하게 된다. 

문법에는 맞지만, 원하는 결과는 아닐 것이다.



현재 블로그에 올리는 예제들은 vim에서 하고 있다. 그래서 본래의 정규식과 이스케이프되야하는 상황이 살짝 다르다. 

이를 테면, 정규식에서는 그냥 + 만 쓰지만, vim에서는 \+ 써야 한다.



이메일 주소를 검색하는 예제

RegEx: 


위의 예제에서 마지막에 \.\w\+ 이 부분을 주의해야 한다. 

이것은, 검색하는 문자열이 맨 마지막에 무조건 .(dot) 다음에 문자가 와야 한다는 것을 명시한 것이다. 저것을 빼면 이메일 주소 마지막에 .(dot) 까지 검색이 된다. 

하지만, 맨 마지막에 위와 같이 명시를 하면 정확히 이메일 주소만 검색을 할 수 있다. 


※ 이 패턴을 보고 집합 안에서는 마침표(.) 를 이스케이프 하지 않아도 마침표와 일치한다는 사실을 알 수 있다. 일반적으로 마침표나 더하기 같은 메타 문자들이 집합의 구성원일 때는 문자 그대로 취급하기 때문에, 굳이 이스케이프 할 필요가 없다. 

그러나 이스케이프 한다고 문제가 생기지는 않는다. [\w.] 는 [\w\.] 와 기능이 같다.




자가 없는 경우나 하나 이상 연속하는 문자 찾기

더하기는 하나 이상 연속된 문자를 찾는다. 문자가 없는 경우는 아예 찾지 못하고, 최소한 하나는 일치해야 한다. 

그래서 있을 수도 있고 없을 수도 있는 문자와 일치시키려면 메타 문자인 별표(*) 를 사용해야 한다.

따라서 B.* Forta 패턴은 B Forta, B. Forta, Ben Forta 같은 조합과 일치한다.


RegEx: 

위와 같은 이메일 주소는 없지만, 가상으로 만들어서 검색을 한 것이다.

맨 처음 \w\+는 처음에 오는 이메일 주소가 유효하고 그다음 . 이 있을 수도 없을 수도 있다는 것이다.




문자가 없거나 하나인 문자 찾기

별표(*) 처럼 물음표(?) 는 문자가 있는 경우 일치하고 문자가 없어도 일치하지만, 별표(*) 와 달리 문자나 집합이 없거나 하나만 있는 경우만 일치하며, 하나 이상은 일치하지 않는다.

즉, 물음표(?) 는 문자 묶음 안에서 있는지 없는지 확실하지 않은 특정한 문자를 하나만 찾을 때 매우 유용하다.


RegEx: 


이 패턴은 https?:// 로 시작한다. 물음표(?)는 자기 앞에 있는 문자가 없거나 그 문자가 하나만 있는 경우 일치한다. 여기서는 s인데, https?:// 는 http:// 나 https:// 와 일치하지만, 그 외에는 일치하지 않는다.


TIP:

집합([]) 은 일반적으로 문자 집합을 정의하는 데 쓰지만, 일부 개발자들은 혼란을 방지하고자 문자가 하나일 때도 집합을 사용한다. 바로 뒤에 나오는 메타 문자가 정확하게 어디에 적용되는지 확실하게 하려는 것이다.

즉, 위의 예제는 아래와 같이 확실하게 명시적으로 쓸 수 있다.

http[s]?://


'Programming > RegEx' 카테고리의 다른 글

Greedy Quantifier & Lazy Quantifier  (0) 2013.05.12
구간 지정  (0) 2013.05.12
포직스(POSIX) 문자 클래스  (0) 2013.05.12
메타 문자 사용  (0) 2013.05.06
문자 집합으로 찾기  (0) 2013.05.06
Posted by scii
:


포직스는 줄여 쓰기를 나타내는 또 다른 형태인데, 전부는 아니지만 많은 정규 표현식 구현에서 지원한다.


POSIX 문자 클래스

POSIX 문자

 [:alnum:]

 [A-Za-z0-9] 알파벳 문자와 숫자로 이루어진 문자열

 [:alpha:]

[A-Za-z] 알파벳 문자 

 [:blank:]

[ /x09] 스페이스와 탭(\t ]와 같다)

 [:digit:]

[0-9] 숫자 

 [:graph:]

[!-~] 공백이 아닌 문자 (스페이스, 제어 문자들을 제외한 문자) 

 [:lower:]

[a-z] 소문자 

 [:print:]

[-~] [:graph:] 와 유사하지만 스페이스 문자를 포함 

 [:punct:]

[!-/:-@[-{-~] 문장 부호 문자([:alnum:]이나 [:cntrl:]가 포함되지 않은 모든 문자

 [:space:]

[ \t\v\f] 모든 공백 문자(newline 줄바꿈, 스페이스, 탭) 

 [:cntrl:]

컨트롤 제어 문자 (아스키 0번부터 31, 127번)

 [:upper:]

[A-Z] 대문자 

 [:xdigit:]

[0-9a-fA-F] 16진수에서 사용할 수 있는 숫자 


※ POSIX

이식 가능 운영체제 인터페이스(portable operating system interface), 또는 포직스는 서로 다른 유닉스 os의 공통 api 를 정리하여, 이식성이 높은 유닉스 응용 프로그램을 개발하려는 목적으로 IEEE가 책정한 애플리케이션 인터페이스 규격이다. 



RegEx: [[:xdigit:]]\+


여기서 사용한 정규 표현식이 [[로 시작해서 ]] 로 끝남을 유의하자. 이는 포직스 클래스를 사용할 때 매우 중요하고 필수적이다. 포직스 클래스는 [:로 시작해 :] 로 끝나기 때문에 우리가 사용하는 포직스 문법은 :xdigit: 가 아니라 [:xdigit:] 이다. 

따라서 바깥에 있는 대괄호는 집합을 정의하고, 안에 있는 대괄호는 포직스 클래스 자체를 나타내는 부분이다.



'Programming > RegEx' 카테고리의 다른 글

구간 지정  (0) 2013.05.12
반복 찾기(+, *) 와 '?' 메타 문자  (0) 2013.05.12
메타 문자 사용  (0) 2013.05.06
문자 집합으로 찾기  (0) 2013.05.06
정규 표현식(Regualr Expression)  (0) 2013.05.05
Posted by scii
:


여기에서 설명할 모듈은 threading이다. 이 모듈은 thread 모듈의 고수준 인터페이스를 지니고 있다. 


가벼운 프로세스(Lightweight Process)라고 하기도 하는 쓰레드는 프로세스 안에서 실행되는 단위이다.

하나의 프로세스 안에 쓰레드는 여러 개 존재할 수 있다. 

하나의 프로세스에 하나의 쓰레드만 존재한다면 싱글 쓰레드 프로세스(Single Thread Process) 이고, 두 개 이상의 쓰레드가 존재한다면 멀티쓰레드 프로세스(Multithread Process) 라고 한다.


쓰레드는 하나의 프로세스 안에 있는 '작은 프로세스'라고 생각할 수 있다. 하나의 프로세스 안에 있는 쓰레드들은 각각 독립적인 스택을 가지고 수행되나, 코드와 자료를 공유한다. 

따라서 쓰레드 간에 자료를 공유, 교환하는 것이 (프로세스 간의 자원 공유에 비해서) 매우 쉽다. 

CPU가 하나이므로 쓰레드의 수행은 어느 시점에서라도 중단되고 다른 쓰레드로 실행권이 넘어갈 수 있다. 쓰레드의 수행은 독립적이어서 다른 쓰레드와의 실행 순서에 관한 어떠한 가정도 할 수 없다.


※ CPython 구현상의 세부사항: Global Interpreter Lock 때문에, 한 번에 하나의 쓰레드만이 실행된다. 따라서 멀티 프로세서에서 threading 모듈을 사용했을 때 성능상의 이점은 전혀 없다. 

병렬 프로그래밍으로 성능을 향상시키고 싶다면 multiprocessing 모듈을 사용해야 한다.

하지만, 성능상의 이점이 없다고 해도 많은 I/O 처리가 동시에 필요할 때 threading 으로 해결할 수 있다.





threading 모듈의 도구:

threading.activeCount()

현재 살아 있는 thread의 개수 반환.


threading.currentThread()

현재 thread 객체를 반환.


threading.enumerate()

현재 살아 있는 모든 thread 반환.


threading.Condition()

새로운 condition variable 객체 반환. condition variable은 내부에 하나의 쓰레드 대기 큐(Queue)를 가진다. wait() 를 호출하는 쓰레드는 이 대기 큐에 넣어지고 대기(Sleep) 상태에 들어간다. notify()를 호출하는 쓰레드는 이 대기 큐에서 하나의 쓰레드를 깨운다.


threading.Event()

Event 객체는 네 개의 메소드를 가지고 있다. ( set(), clear(), wait(), isSet() ) Event 객체는 내부에 하나의 이벤트 플래그를 가진다. 초기 값은 0이다.

set() 메소드는 내부 플래그를 1로 만들어 주고, clear() 메소드는 0으로 만든다.

wait() 메소드는 내부 플래그가 1이면 즉시 리턴되며, 0이면 다른 스레드에 의해서 1이 될 때까지 블록(대기) 상태에 들어간다. wait()는 내부 플래그 값을 바꾸지 않는다.


threading.Lock()

lock 객체를 생성. 하나의 스레드가 정보를 갱신하는 동안 다른 스레드가 그 변수에 접근하지 못하도록 해야 할 때 쓰인다.


threading.RLock()

RLock 클래스 객체는 Lock 객체와 같으나, 락을 소유하고 있는 스레드가 두 번 이상 acquire와 release를 호출 할 수 있다. acquire 한 만큼 release 해야 락이 해제된다.


threading.Semaphore([value])

semaphore 객체는 생성. 세마포어는 카운터로 관리한다. 카운터는 acquire() 메소드를 사용하면 -1 이 되고, release() 메소드를 사용하면 +1 이 된다. 

만약, acquire()를 실행할 때 카운터 값이 0이면 스레드는 세마포 변수 대기 큐에 넣어져 블럭 상태(Block, 실행을 멈추고 어떤 사건이 일어나기를 대기하는 상태. CPU를 점유하지 않는다.) 로 들어간다.

release() 는 우선 세마포 변수에 대기하고 있는 쓰레드가 있는지를 검사한다. 만일 스레드가 존재하면 그들 중 가장 오래 대기하고 있던 스레드 하나를 풀어 준다. 스레드가 대기 큐에 없으면 카운터 값을 단순히 1 증가한다.


threading.BoundedSemaphore([value])

bounded semaphore 객체를 생성. bounded semaphore 란 현재 값이 초기 값을 초과하지 않는지 검사하는 기능이 추가되어 있다. 초기 값을 초과할 경우 ValueError 예외가 발생한다. 제한된 자원을 관리할 때 사용된다.


threading.local -> Class

thread의 local data를 나타내는 class이다. thread local 데이터를 관리하기 위해서는 local로 인스턴스를 생성하면 된다.

data = threading.local()

data = 1

스레드마다, 인스턴스의 값이 다르다.


threading.Thread -> Class

thread를 조작할 때 사용하는 클래스이다.


threading.Timer -> Class

일정 시간 간격마다 스레드에서 특정 함수를 실행하도록 하기 위해 사용한다.


threading.stack_size([size])

새로운 스레드를 생성했을 때 스레드의 스택 크기를 반환한다.



Thread 객체

class threading.Thread(group=None, target=None, name=None, args=(), kwargs={})

키워드로 인자를 전달하는 Thread의 생성자

group은 반드시 None, 차후확을 고려한 것이다.

target은 run() 메소드가 호출될 때 실행할 함수를 지정한다.  기본은 None이다.

name은 thread의 이름을 뜻한다. 기본으로 'Thread-N' 형태로 지어진다.

args는 target에 전달할 인자를 tuple형태로 받는다.

kwargs는 target에 전달할 인자를 dictionary 형태로 받는다.

subclass를 만들 때 생성자를 override 하려면, Thread.init() 를 가장 먼저 실행하도록 해야 한다.


start()

thread를 시작한다. 

thread마다 한번씩 실행해야 한다. 이 메소드는 run() 메소드를 실행하도록 해준다. 

동일한 thread에 한번 이상 실행하면 RuntimeError 예외 발생.


run()

run() 메소드는 target 함수를 호출한다. override 가능.


join([timeout])

thread 가 종료할 때까지 기다린다. join() 메소드가 실행된 지점에서 thread가 종료할 때까지 기다린다.

기본 timeout 인자는 None 이다. float point나 fraction 형으로 인자를 초 단위로 지정할 수 있다. join() 은 언제나 None 을 반환한다. 만약 join() 을 호출한 뒤 언제 timeout이  발생하는지 알고 싶다면, isAlive() 를 호출해야 한다. timeout이 지난 다음에 아직 thread가 살아 있다면 thread는 종료된다.

join은 여러번 호출 될 수 있다.

현재 thread가 deadlock을 유발하려고 하면 join()은 RuntimeError 를 발생시킨다. 또는 시작하지 않은 thread에서 join()을 호출해도 마찬가지 예외가 발생한다.


name

이 문자열은 식별하기 위한 용도로만 사용하고 특별한 의미는 없다. 여러 thread가 같은 이름을 가져도 상관 없다. 초기 이름은 생성자에 의해 지정된다.


ident

특정 thread의 식별자이다. thread가 시작하지 않았다면 None으로 되어 있지만 시작하면 0이 아닌 어떤 정수를 가진다. thread.get_ident() 함수로 읽어 올 수 있다. 새로운 thread가 생성되면 이미 종료된 thread의 ident 값이 재활용될 수 있다.


isAlive()

thread 가 살아 있는지 여부를 반환한다.

run() 메소드가 실행된 다음부터 run() 메소드가 종료되는 때까지 True 값을 반환한다. 모듈 함수 enumerate() 는 살아 있는 thread의 list를 반환한다.


daemon

현재 thread가 daemon thread인지 아닌지 boolean 값으로 표시되어 있다. daemon 일 경우 True이다. start() 메소드가 실행되기 전에 지정되어야 하며, 그렇지 않다면 RuntimeError가 발생한다. 초기값을 부모 thread로 부터 상속 받는다. 따라서 기본으로 False로 되어 있다. 

모든 python 프로그램은 daemon이 아닌 thread가 모두 종료되면 종료된다. daemon인 thread는 계속 실행되는 상태에서 프로그램은 종료된다.


각각 분리된 thread를 관리하기 위한 클래스이다. 쓰레드를 생성하는 방법은 두 가지가 있다.

1) 호출 가능한 객체(함수 등) 를 생성자에 직접 전달하는 방법

2) 서브 클래스에서 run 메소드를 중복하는 방법


thread 객체가 생성되고 나면 start() 메소드를 실행해야 thread가 실행된다. start() 메소드는 각각의 thread에서 run() 메소드를 호출한다.

thread 객체가 시작되면, thread는 'alive' 상태가 된다. 이것은 run() 메소드가 종료되거나 예외가 발생 했을 때 종료된다. is_alive() 메소드를 실행하여 살아 있는지 여부를 알 수 있다. 

join() 메소드를 호출하여 thread 가 종료될 때까지 기다리도록 할 수 있다. 





상호 배제

멀티쓰레딩을 하면, 값을 갱신하는 중간에 연산이 다른 쓰레드로 교체되면서 바르지 못한 정보가 유지되지 못할 수 도 있다. 

이 같은 문제점을 해결하려면 하나의 쓰레드가 정보를 갱신하는 동안 다른 쓰레드가 그 변수에 접근하지 못하도록 해야 한다. 모듈 threading은 락(Lock) 객체를 지원한다. 


락 객체는 다음의 세가지 메소드를 가진다.

threading.Lock().acquire()

락을 얻는다. 일단 하나의 스레드가 락을 얻으면 다른 스레드는 락을 얻을 수 없다. 락을 소유하고 있는 스레드가 release()를 호출할 때까지 대기한다. 락이 해제되면 락을 얻는다.


threading.Lock().release()

락을 해제한다. 만일 acquire()로 해당 락을 기다리는 스레드가 있으면 그 스레드 중 하나만 락을 얻을 수 있다. 


threading.Lock().locked()

락을 얻었으면 1, 아니면 0을 리턴한다.


전형적인 사용 예)

전역 변수로 한 번만 lock을 얻는다. 

lock = threading.Lock()             # 여기서 얻어진 lock 객체는 모든 스레드가 공유해야 한다.


각 스레드는 다음과 같은 코드를 수행한다.

lock.acquire()                        # 락을 얻고 들어 간다. 이미 다른 스레드가 들어 가 있으면 락을 얻을 때까지 여기서 자동적으로 대기한다.

g_cnt += 1                             # 필요한 코드를 수행한다. 상호 배제 영역(Critical Section)

lock.release()                       # 락을 해제한다. 다른 스레드가 이 코드 영역으로 들어 갈 수 있도록 허락한다.


이와 같이 상호 배타적으로 코드를 수행해야 할 영역을 임계 영역(Critical Section) 이라고 한다. 

이러한 상호 배제는 락 객체로 구현 가능하다. 


위의 코드를 클래스로 작성함. 똑같은 결과.



Semaphore 객체

세마포는 가장 오래된 동기화 프리미티브이다. 세마포는 내부에 정수형의 카운터 변수를 가지고 있으며, 이 값은 세마포 변수를 생성할 때 초기화된다.

acquire()에 의해 1씩 감소하고, release()에 의해 값이 1씩 증가한다. 카운터는 0보다 작은 값을 가질 수 없다.


Semaphore 변수 활용 예

어떤 임계 영역이 있다고 하자. 이 영역에 단지 몇 개의 스레드만이 진입할 수 있도록 허용하고 싶다. 다음 예는 100개의 스레드가 어떤 코드를 500회 반복한다. 반복하는 코드의 어떤 영역은 최대 3개의 스레드만이 진입할 수 있도록 허용한다.




Event 객체

Event 객체는 네 개의 메소드를 가지고 있다. (set(), clear(), wait(), isSet()) Event 객체는 내부에 하나의 이벤트 플래그를 가진다. 초기 값은 0이다.

set() 메소드는  내부 플래그를 1로 만들어 주고, clear()메소드는 0으로 만든다.

wait() 메소드는 내부 플래그가 1이면 즉시 리턴되며, 0이면 다른 스레드에 의해서 1이 될 때까지 블록(대기) 상태에 들어간다. wait()는 내부 플래그 값을 바꾸지 않는다.


Event 변수 활용 예

두가지 종류의 스레드가 있다고 하자. 하나는 뭔가를 준비하는 스레드(T1) 이고, 나머지는 준비된 환경 하에서 수행하는 스레드이다. 당연히 T1이 원하는 작업을 수행한 후에 나머지 다른 스레드가 진행되어야 할 것이다. 

이때 Event 동기화를 이용하면 쉽게 문제가 해결된다.


'Programming > Python' 카테고리의 다른 글

multiprocessing Module  (0) 2013.05.17
Regular Expression  (0) 2013.05.13
발생자(Generator)  (0) 2013.05.09
반복자(Iterator)  (0) 2013.05.09
class 메소드 관련  (0) 2013.05.08
Posted by scii
:


발생자

발생자는 icon이란 언어에서 영향을 받았다.

만일 함수가 호출된 후에 되돌아갈 때, 메모리가 해제되지 않고 그대로 남아 있다면 어떨까? 그리고 다시 그 함수가 호출될 때 이전에 수행이 종료되었던 지점 이후를 계속 진행한다면 어떨까?     =>     이것이 발생자이다.

발생자란 (중단된 시점부터) 재실행 가능한 함수라고 할 수 있다. 


def generate_ints(n):

for i in range(n):

yield i

yield 는 발생자를 위해서 파이썬 2.2에 새롭게 도입된 예약어이다. 어떤 함수이든 yield 를 가지고 있다면 발생자이다.

파이썬 컴파일러가 이 키워드를 검출하면 이 함수를 발생자로 만들기 위해 별도로 처리한다.

yield 는 return 과 유사하나 실행 상태를 보존한 상태에서 복귀한다. 

gen 발생자 객체는 반복자이다. 

generate_ints의 초기 스택 프레임이 만들어지나, 실행은 중단된 상태이다.

gen.next() 로 실행이 재개되며, yield 에 의해서 중단된다. 

yield 는 일반 함수의 return 문과 같이 값을 호출 측에 돌려주나, 실행 상태와 지역 변수들을 보존한 상태로 값을 출력하는 것이 차이점이다.

발생자 객체의 next() 메소드 호출로 yield 다음 문장부터 다시 수행을 계속한다.


따라서 발생자를 다음과 같이 for 루프에 직접 사용하는 것이 가능하다.

일반 함수와 발생자의 가장 큰 차이점:

일반 함수는 호출되면, 종료할 때까지 모든 일을 마친 결과를 넘겨야 한다.

발생자는 정보를 생성하는 중간에 결과를 넘길 수 있다.


이것은 어떤 작업의 중간 결과를 다른 코드에서 참조해야 할 경우에 유용하게 활용된다. 모든 결과를 한꺼번에 받아서 처리하기보다는 중간 중간에 나오는 결과를 사용한다면 발생자가 효고적으로 활용될 수 있다.

병행 프로세스나 혹은 멀티 쓰레드로 처리해야 할 일부 작업들을 발생자로 대치할 수도 있다. 

트리 탐색이나 토큰 분석기 등이 대표적으로 적용 가능한 예가 될 것 같다.


발생자 구문

파이썬 2.2에서 도입된 반복자(Iterator)로 많은 양의 자료를 처리할 때 자료를 모두 메모리에 올리지 않고 순차적으로 반복 처리하는 것이 가능하게 되었다.

리스트 내장은 리스트 객체를 새로 생성하므로 메모리를 낭비하는 문제가 있다.

리스트 내장의 대부분의 경우는 발생자 구문으로 대치될 수 있다.

중간 리스트를 생성, 저장해야 하는 경우가 아니면 발생자 구문이 대부분 더 좋은 성능을 발휘한다.



발생자의 활용

피보나치 수열


홀수 집합 만들기

※ 위의 두 개를 테스 해 본 결과, class로 만든 것이 0.5초 더 빨랐다.

list(odds(20)) 같은 경우, 가장 느렸다.


하나씩 건너뛰어서 값 취하기


혹은 itertools 모듈을 이용하면 다음과 같은 코딩도 가능하다.


트리 탐색

이진 트리에서 깊이 inorder 탐색 기법으로 모든 노드들을 탐색하는 메소드를 구현.

파이썬에서 트리를 표현하는 것은 매우 간단하다. __init__ 메소드에서처럼 좌우 노드(left, right) 그리고 데이터를 가지는 멤버로 충분히 표현된다. Iorder 탐색은 inorder 메소드로 구현된다. 이 메소드는 생성자로 구현되었다. 

한 가지 주의할 것은 __iter__ 메소드이다. 이 메소드는 inorder 를 즉, inorder 생성자를 반복자로 리턴하고 있다. 트리에 대한 반복자는 곧 inorder 발생자가 된다.

따라서 마지막 두 줄처럼 마치 트리를 리스트나 튜플과 같이 수비게 for루프에서 활용할 수 있게 된다. 이ㅓㅅ으로 아주 일반화된 트리 탐색 방법을 가지게 된다. 이것은 생성자의 큰 장점이다.


중첩 리스트를 단일 리스트로 만들기

1) 발생자를 이용하지 않은 재귀적인 방법


2) 발생자를 이용한 방법


※ 발생자는 시퀀스 연산이 적용되는 곳이면 어디나 사용할 수 있다. 


'Programming > Python' 카테고리의 다른 글

Regular Expression  (0) 2013.05.13
Multithread Process  (0) 2013.05.12
반복자(Iterator)  (0) 2013.05.09
class 메소드 관련  (0) 2013.05.08
협동적 메소드와 super()  (0) 2013.05.07
Posted by scii
:


순차적으로 참조는 하나, 인덱싱에 의한 순서적인 참조가 의미 없는 경우를 보완하기 위해 반복자(Iterator)가 만들어졌다.


반복자 객체의 특성

# 반복자 객체는 iter() 내장 함수로 만들어진다.

# 반복자 객체는 next() 메소드를 가진다. next() 메소드를 가지는 객체가 반복자 객체이다.

# 반복자 객체가 next() 메소드로 더 이상 자료를 넘겨줄 수 없는 경우에는 StopIteration 예외를 발생한다.


클래스에 반복자 구현하기

이와 같이 순차적으로 참조되어야 할 정보는 반복자로 구현되는 것이 타당하다.



사전의 반복자

iterkeys, itervalues, iteritems 메소드는 키, 값 혹은 (키, 값) 튜플의 반복자를 리턴한다.

keys(), values(), items() 메소드를 이요해서 리스트로 된 자료를 얻은 후에 for 루프를 돌릴 수도 있지만 iterkeys(), itervalues(), iteritems() 메소드를 이용하면 요구된 시점에서 원하는 자료를 그때 그때 만들어서 넘겨주므로 리스트를 미리 구성해야 하는 부담이 없다. 

또 언제든지 루프를 중단할 때는 나머지 데이터에 대한 참조를 하지 않아도 되므로 더 효율적인 수행이 가능하다.



파일 객체의 반복자

파일 객체가 라인 단위의 반복자를 지원한다. 앞서 Seq 와 같은 클래스를 만들 필요가 없다.



호출 가능한 객체에 반복자 적용하기

호출 가능한 객체(callable) 에 반복자를 적용할 수 있다.


C언어에는 다음과 같은 구조가 있다. (엔터 키만 입력 받으면 Null을 리턴)

while((line = readline()) != NULL)

{

printf(line);

}

이러한 구조를 파이썬으로 구현하기는 쉽지 않은데, 다음과 같이 하면 가능하다.

for line in iter(raw_input, ''):

print line

호출되는 함수가 인수를 받을 경우에는 위와 같은 방법으로는 안된다. 이 경우는 람다 함수를 이용하면 쉽게 해결 된다.

lambda:accum(sum, 2) 와 같이 lamda: 다음에 원하는 함수를 호출해 주면 된다. 매번 함수를 호출한 결과 값은 sum이 받을 것이며, 이 값은 40 과 비교되어 값이 같으면 루프를 종료한다.



itertools 모듈

이 모듈은 반복 가능한 객체(시퀀스, 반복자, 발생자등) 의 반복자(혹은 발생자)를 생성하는 몇 개의 함수로 구성되어 있다.

이들 함수는 메모리와 계산 시간에서 효과적인 연산이 가능하도록 많은 도움을 준다. 반복자가 효과적인 이유는 자료가 욕구될 때 필요한 시점에서 자료를 준비해서 리턴하기 때문이다.

기에 설명되는 함수들은 자료의 크기가 크면 클수록 더욱 그 효과를 발휘한다.


chain(*iterables) 함수는 객체에서 자료를 연속해서 하나씩 넘겨준다.

l1, l2에 대해 연속된 데이터를 넘겨준다.


이 방법은 새로운 리스트를 생성하므로 메모리를 낭비한다.



groupby(iterable[, key]) 는 자료를 그룹 단위로 묶는 데 사용된다. key 함수가 주어지지 않았을 경우는 요소 자료 자체가 키 값이 된다.

인수가 정렬되어야 인접한 자료를 한 그룹으로 묶는다.


두 번째 자료를 기준으로 그룹화하려면 다음과 같이 할 수 있다.

itemgetter(1) 은 어떤 객체의 [1] 요소 값을 취하는 함수이다. 즉, lambda x:x[1] 과 같은 함수이다.


다음 예는 문장의 단어가 몇 번 반복되는가를 출력한다.


'Programming > Python' 카테고리의 다른 글

Multithread Process  (0) 2013.05.12
발생자(Generator)  (0) 2013.05.09
class 메소드 관련  (0) 2013.05.08
협동적 메소드와 super()  (0) 2013.05.07
연산자 오버로딩  (0) 2013.05.02
Posted by scii
:



실행 결과





실행 결과


'Programming > Python' 카테고리의 다른 글

발생자(Generator)  (0) 2013.05.09
반복자(Iterator)  (0) 2013.05.09
협동적 메소드와 super()  (0) 2013.05.07
연산자 오버로딩  (0) 2013.05.02
생성자와 소멸자  (0) 2013.05.02
Posted by scii
: