'Programming/Python'에 해당되는 글 79건

  1. 2013.05.12 Multithread Process
  2. 2013.05.09 발생자(Generator)
  3. 2013.05.09 반복자(Iterator)
  4. 2013.05.08 class 메소드 관련
  5. 2013.05.07 협동적 메소드와 super()
  6. 2013.05.02 연산자 오버로딩
  7. 2013.05.02 생성자와 소멸자
  8. 2013.05.02 class member, instance member


여기에서 설명할 모듈은 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
:


협동적 클래스(Cooperative Classes) 란?

'협동적 super call'을 호출하는 다중 상속 관계에 있는 클래스들이다.


기존 클래스의 문제점

기존 클래스의 문제점을 살펴보자. 이것은 파이썬만의 문제가 아니라 대부분 객체지향 언어에서 발생할 수 있는 문제이다.


예를 들어, 어떤 클래스(A)가 주어져 있고, 서브 클래스(B)에서 슈퍼 클래스의 save 메소드를 확장하려 한다.

class A:

def save(self):

print 'A save call'

class B:

def save(self):

print 'B save call'

A.save(self)


A의 save는 A만의 자료를 저장하고 B의 save는 B만의 자료를 저장한다. 따라서 클래스 B의 save는 A.save 를 호출한다.

그러나 다이아몬드 관계인 경우 문제가 발생한다. 다음 클래스를 살펴보자.

이것을 수행하면 A의 데이터가 두 번이나 저장된다. (B에 의해 한 번, C에 의해 한 번)


위의 것을 현재 알고 있는 지식으로만 해결하면 다음과 같이 할 수 있겠다. 

즉, 어떤 클래스의 save는 자신의 정보만을 저장하는 self._save()를 호출한 후에 슈퍼 클래스의 지역 정보를 저장하는 메소드를 일일이 호출해야 한다.

해결은 되었지만, 누가 봐도 좋은 코드라고 말할 수 없다. 클래스 D는 슈퍼 클래스들의 모든 구조를 알아야 save 메소드를 작성할 수 있다. 이러한 내용은 객체지향 언어가 추구하고 있는 방향과는 상반된 것이다.

또한 코드의 이식성이 크게 떨어지며 나중에 C를 제거하거나, 또 다른 클래스 E를 슈퍼 클래스로 추가하려면 D의 코드는 다시 작성되어야 한다.



협동적 클래스를 이용한 문제 해결

앞서와 같은 문제는 super를 이용한 협동적 클래스로 해결이 가능하다.

super()는 다음과 같은 형식을 가진다.

super(class name, self).method()

의미는 슈퍼 클래스의 '메소드'를 호출하라는 것이다. 슈퍼 클래스는 여러 개가 있을 수 있다. 따라서 어떤 슈퍼 클래스를 호출하는가는 self.__class__.__mro__ 와 '클래스 이름'을 이용하여 결정된다.

를 들어, self.__class__.__mro__ 가 (w, x, y, z)이고 '클래스 이름'이 'x'일 경우 'x' 다음에 있는 'y'메소드가 호출된다.


구체적인 예)


클래스의 __mro__


super는 self.__class__.__mro__의 순서에 따라 save()를 호출한다고 했다. super는 self.__class__.__mro__에서 자신의 클래스 다음의 클래스 객체의 save를 호출한다.

예를 들어, super(C, self).save() 에서 전달된 self.__class__.__mro__가 (D, B, C, A, object) 라면, A.save(self) 를 호출한다.


단계적으로 설명:

1) d.save()에 의해서 super(D, self).save()가 호출된다. .self는 d이고 d.__class__.__mro__가 (D, B, C, A, object)이므로 super(D, self).save()에 의하여 B.save(self)가 호출된다.

2) B.save(self)에 의해서 super(B, self).save()가 호출된다. 이 때 self 는 여전히 'd'  임에 주의해야 한다. self.__class__.__mro__는 여전히 (D, B, C, A, object) 값을 가진다. 따라서, super(B, self).save()는 B 다음 클래스인 C 에서 C.save(self)를 호출한다.

3) 같은 방법으로 super(C, self).save()는 A.save(self)를 호출한다. 


이와 같이 super를 사용하면 앞서 해결할 수 없었던 슈퍼 클래스 메소드의 호출 문제를 깔끔하게 해결할 수 있다.

클래스의 전체 구조를 모르고도 단지 '슈퍼 클래스의 이러한 메소드를 호출하라' 식의 표현으로도 충분히 잘 동작하는 코드를 작성할 수 있다.


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

반복자(Iterator)  (0) 2013.05.09
class 메소드 관련  (0) 2013.05.08
연산자 오버로딩  (0) 2013.05.02
생성자와 소멸자  (0) 2013.05.02
class member, instance member  (0) 2013.05.02
Posted by scii
:


파이썬에서는 다른 자료형에 적용되는 모든 연산을 사용자가 정의하는 클래스에서 동작할 수 있도록 구현할 수 있다.


 ex) 문자열 나누기


__div__ 메소드는 나누기 연산자 '/'가 사용되었을 때 자동으로 호출된다.


파이썬은 객체 m이 __div__ 메소드를 가지고 있는가를 동적으로 검사한다. 만일 메소드를 가지고 있다면 'b'를 인수로 __div__ 메소드를 호출한다.

즉, 다음과 같이 메소드를 호출한다.

# m / 'b' 와 같은 코드

m.__dive__('b')


만일 나눗셈의 피연산자 순서를 바꾸면 어떻게 될까?

예외가 발생한다.!! 객체가 오른쪽으로 가면, __rdiv__ 메소드를 호출하기 때문이다.

그래서 __rdiv__를 정의해야 한다.

예제에서는 __div__와 __rdiv__ 메소드 내부가 동일하다. 

따라서, 다음과 같이 간단히 메소드를 정의할 수 있다.

__rdiv__ = __div__


수치 연산자 메소드 


※ 피연산자가 바뀐 경우의 수치 연산자 메소드 이름앞에 'r' 만 추가해주면 된다.

ex) __rshift__     =>     __rrshift__


※ 확장 산술 연산자 메소드 (+=, -=, *= 등등...)

메소드 이름앞에 'i' 만 추가해주면 된다.

ex) __add__     =>     __iadd__


 Method 

 Operator

 __add__(self, other) 

 +

 __sub__(self, other) 

 - 

 __mul__(self, other) 

 * 

 __div__(self, other) 

 / 

 __truediv__(self, other) 

 from __future__ import division이 실행되었다면, '/' 연산자에 의해 이 메소드 호출

 __floordiv__(self, other) 

 // 

 __mod__(self, other) 

 __divmod__(self, other) 

 divmod() 

 __pow__(self, other[, module]) 

 pow(), ** 

 __lshift__(self, other) 만

 << 

 __rshift__(self, other) 

 >> 

 __and__(self, other) 

 & 

 __xor__(self, other) 

 ^ 

 __or__(self, other) 

 | 



수치 단항 연산자 메소드

 Method

 Operator 

 __neg__(self) 

 - 

 __pos__(self) 

 + 

 __abs__(self) 

 abs() 

 __invert__(self) 

 ~ 비트 반전 (0은 1로, 1은 0으로) 


기타 형 변환 메소드

다음의 연산자들은 객체를 인수로 하여 함수 형태로 호출된다. 그에 따른 적절한 값을 리턴해 줘야 한다. 

예를 들면, int(a) 에 의해서 a.int() 가 호출된다.

.__oct__, .__hex__ 인 경우는 문자열을 리턴한다.

Method

 Opereator 

 __complex__(self) 

 complex()   

 __int__(self) 

 int() 

 __long__(self) 

 long() 

 __float__(self)  

 float() 

 __oct__(self) 

 oct() 

 __hex__(self) 

 hex() 



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

class 메소드 관련  (0) 2013.05.08
협동적 메소드와 super()  (0) 2013.05.07
생성자와 소멸자  (0) 2013.05.02
class member, instance member  (0) 2013.05.02
정적 메소드, 클래스 메소드, 장식자  (0) 2013.05.01
Posted by scii
:


일반적으로클래스는 생성자(constructor)와 소멸자(destructor)라 불리는 메소드를 정의할 수 있게 되어 있다. 


생성자는 인스턴스 객체가 생성될 때 초기화를 위해서 자동적으로 불려지는 초기화 함수를 말하고,

소멸자는 인스턴스 객체가 사용이 끝나서 메모리에서 해제될 때 자원 해제를 위해서 자동으로 호출되는 함수를 말한다.


파이썬 클래스에서는 생성자와 소멸자를 위해 특별한 이름을 준비해 놓고 있다.

생성자 함수 이름은 __init__ 

소멸자 함수 이름은 __del__


※     일반적으로 파이썬에서는 어떤 이름 앞뒤로 _(언더라인)이 두 개씩 붙어 있으면, 예약어(Reserved Words) 이다.

클래스 메소드로써는 미리 어떤 기능이 정의된 경우를 의미한다.



생성자는 대부분 인스턴스 멤버를 생성하고 값을 초기화하는 데 주로 사용된다.

소멸자는 실제로 많이 활용되지는 않는다. 왜냐하면, 대부분의 메모리나 기타 자원 관리가 자동으로 되기 때문에 특별히 신경 쓰지 않아도 인스턴스가 소멸하면서 자원이 원상 복귀되기 때문이다.


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

협동적 메소드와 super()  (0) 2013.05.07
연산자 오버로딩  (0) 2013.05.02
class member, instance member  (0) 2013.05.02
정적 메소드, 클래스 메소드, 장식자  (0) 2013.05.01
메쏘드의 정의와 호출  (0) 2013.05.01
Posted by scii
:


멤버에는 두 가지가 있다. 클래스 멤버와 인스턴스 멤버이다.


클래스 멤버는 클래스 이름 공간에 생성된다.

클래스 멤버는 모든 인스턴스 객체들에 의해서 공유될 수 있다.

인스턴스 멤버는 인스턴스 객체 이름 공간에 생성된다.

인스턴스 멤버는 각각의 인스턴스 객체 내에서만 참조된다.



클래스 멤버는 메쏘드 바깥에 정의된다. 

인스턴스 멤버는 메소드 내부에서 self를 이용하여 정의된다.

클래스 내부에서 멤버들을 참조할 때는 self.c_mem, self.i_mem 과 같이 한다.


외부에서 참조할 때...

클래스 멤버는 클래스이름.멤버이름 형식으로 호출하며, 인스턴스 멤버는 인스턴스이름.멤버이름 형식으로 호출한다.

클래스 멤버는 인스턴스이름.멤버이름 형식으로 호출하는 것이 가능하다.

단! 인스턴스 이름 공간에 같은 이름의 멤버가 없어야 한다. 인스턴스 이름 공간의 멤버를 우선적으로 처리하기 때문이다.


검색 순서

우선적으로 인스턴스 멤버를 참조한다.

만일, 인스턴스 멤버가 존재하지 않으면 클래스 멤버를 참조한다.


클래스 멤버는 모든 클래스 인스턴스가 공유하는 멤버이고, 

인스턴스 멤버는 각각의 클래스 인스턴스가 별도로 가지고 있는 멤버이다. 즉, 각 인스턴스의 특성을 나타낸다고 할 수 있다.


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

연산자 오버로딩  (0) 2013.05.02
생성자와 소멸자  (0) 2013.05.02
정적 메소드, 클래스 메소드, 장식자  (0) 2013.05.01
메쏘드의 정의와 호출  (0) 2013.05.01
class  (0) 2013.05.01
Posted by scii
: