#!/usr/bin/env python # coding: utf-8 # *** # *** # # 20. 클래스와 연산자 중복 정의 # *** # *** # *** # ## 1 연산자 중복 (Operator Overloading) # *** # ### 1-1 수치 연산자 중복 # - 직접 정의하는 클래스 인스턴스에 연산자를 적용하기 위하여 미리 약속되어 있는 메소드들을 정의 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

메소드(Method)

연산자(Operator)

인스턴스 o에 대한 사용 예

\_\_add\_\_(self, B)

+ (이항)

o + B, o += B

\_\_sub\_\_(self, B)

- (이항)

o - B, o -= B

\_\_mul\_\_(self, B)

\*

o \* B, o \*= B

\_\_div\_\_(self, B)

/

o / B, o /= B

\_\_floordiv\_\_(self, B)

//

o // B, o //= B

\_\_mod\_\_(self, B)

%

o % B, o %= B

\_\_divmod\_\_(self, B)

divmod()

divmod(o, B)

\_\_pow\_\_(self, B)

pow(), \*\*

pow(o, B), o \*\* B

\_\_lshift\_\_(self, B)

<<

o << B, o <<= B

\_\_rshift\_\_(self, B)

>>

o >> B, o >>= B

\_\_and\_\_(self, B)

&

o & B, o &= B

\_\_xor\_\_(self, B)

^

o ^ B, o ^= B

\_\_or\_\_(self, B) 

|

o | B, o |= B

\_\_neg\_\_(self)

- (단항)

-A

\_\_abs\_\_(self)

abs()

abs(o)

\_\_pos\_\_(self)

+ (단항)

+o

\_\_invert\_\_(self)

~

~o

# In[1]: class Integer: def __init__(self, i): self.i = i def __str__(self): return str(self.i) def __add__(self, other): return self.i + other i = Integer(10) print i print str(i) print i = i + 10 print i print i += 10 print i # In[2]: class MyString: def __init__(self, str): self.str = str def __div__(self, sep): # 나누기 연산자 /가 사용되었을 때 호출되는 함수 return self.str.split(sep) # 문자열 self.str을 sep를 기준으로 분리 m = MyString("abcd_abcd_abcd") print m / "_" print m / "_a" print print m.__div__("_") # - 연산자 왼쪽에 피연산자, 연산자 오른쪽에 객체가 오는 경우 # - 메소드 이름 앞에 r이 추가된 메소드 정의 # In[3]: class MyString: def __init__(self, str): self.str = str def __div__(self, sep): return str.split(self.str, sep) __rdiv__ = __div__ m = MyString("abcd_abcd_abcd") print m / "_" print m / "_a" print print "_" / m print "_a" / m # In[4]: class MyString: def __init__(self, str): self.str = str def __div__(self, sep): return str.split(self.str, sep) __rdiv__ = __div__ def __neg__(self): t = list(self.str) t.reverse() return ''.join(t) __invert__ = __neg__ m = MyString("abcdef") print -m print ~m # ### 1-2 비교 연산자 중복 # - 각각의 비교 연산에 대응되는 메소드 이름이 정해져 있지만 그러한 메소드가 별도로 정의되어 있지 않으면 cmp가 호출됨 # # |메소드 |연산자 | 비고 | # |--------|----|----| # |\_\_cmp\_\_(self, other) | 아래 메소드가 부재한 상황에 호출되는 메소드| | # |\_\_lt\_\_(self, other) | self < other | | # |\_\_le\_\_(self, other) | self <= other | | # |\_\_eq\_\_(self, other) | self == other | | # |\_\_ne\_\_(self, other) | self != other | | # |\_\_gt\_\_(self, other) | self > other | | # |\_\_ge\_\_(self, other) | self >= other | | # - 객체 c에 대한 c > 1연산의 행동 방식 # - c.\_\_gt\_\_()가 있다면 호출 결과을 그대로 반환 # - 정의된 c.\_\_gt\_\_()가 없고, \_\_cmp\_\_() 함수가 있을 경우 # - c.\_\_cmp\_\_() 호출 결과가 양수이면 True 반환, 아니면 False 반환 # In[5]: class MyCmp: def __cmp__(self, y): return 1 - y c = MyCmp() print c > 1 # c.__cmp__(1)을 호출, 반환값이 양수이어야 True print c < 1 # c.__cmp__(1)을 호출, 반환값이 음수이어야 True print c == 1 # c.__cmp__(1)을 호출, 반환값이 0이어야 True # - 객체 m에 대한 m < 10연산의 행동 방식 # - m.\_\_lt\_\_()가 있다면 호출 결과을 그대로 반환 # - 정의된 m.\_\_lt\_\_()가 없고, \_\_cmp\_\_() 함수가 있을 경우 # - m.\_\_cmp\_\_() 호출 결과가 음수이면 True 반환, 아니면 False 반환 # In[6]: class MyCmp2: def __lt__(self, y): return 1 < y m = MyCmp2() print m < 10 # m.__lt__(10)을 호출 print m < 2 print m < 1 # - 객체 m에 대한 m == 10연산의 행동 방식 # - m.\_\_eq\_\_()가 있다면 호출 결과을 그대로 반환 # - 정의된 m.\_\_eq\_\_()가 없고, \_\_cmp\_\_() 함수가 있을 경우 # - m.\_\_cmp\_\_() 호출 결과가 0이면 True 반환, 아니면 False 반환 # In[7]: class MyCmp3: def __eq__(self, y): return 1 == y m = MyCmp3() print m == 10 # m.__eq__(10)을 호출 m1 = MyCmp3() print m == 1 class MyCmp4: def __init__(self, value): self.value = value def __cmp__(self, other): if self.value == other: return 0 m2 = MyCmp4(10) print m2 == 10 # *** # ## 2 시퀀스/매핑 자료형의 연산자 중복 # *** # - 클래스를 개발할 때 다음 메소드들을 적절하게 구현하면 자신만의 시퀀스 자료형을 만들 수 있음 # - 시퀀스 자료형 및 매핑 자료형을 위해 구현이 필요한 메소드 # |메소드 |연산자 | # |---|---| # |\_\_len\_\_(self) | len() | # |\_\_contains\_\_(self, item) | item in self | # |\_\_getItem\_\_(self, key) | self[key] | # |\_\_setItem\_\_(self, key, value) | self[key] = value | # |\_\_delItem\_\_(self, key) | del self(key) | # ### 2-1 인덱싱 (Indexing) # - len(s1) --> s1.\_\_len\_\_() 메소드 호출 # - sl[4] --> s1.\_\_getitem\_\_(4) 호출 # - IndexError # - 시퀀스 자료형이 범위를 벗어난 인덱스 참조 요구시에 발생시킴 # - 리스트, 튜플, 문자열등에서도 동일한 조건에서 발생됨 # In[8]: class Square: def __init__(self, end): self.end = end def __len__(self): return self.end def __getitem__(self, k): if k < 0 or self.end <= k: raise IndexError, k return k * k s1 = Square(10) print len(s1) # s1.__len__() print s1[1] #s1.__getitem__(1) print s1[4] print s1[20] # - 다음 for 문은 s1에 대해 \_\_getitem()\_\_ 메소드를 0부터 호출하여 IndexError가 발생하면 루프를 중단한다. # In[9]: for x in s1: print x, # - \_\_getitem\_\_() 메소드가 정의되어 있다면 다른 시퀀스 자료형으로 변환이 가능 # In[10]: print list(s1) print tuple(s1) # - 위에서 알 수 있듯이 파이썬은 내장 자료형과 개발자가 정의한 자료형에 대해 일관된 연산 적용이 가능 # - 파이썬 언어의 장점: 일관된 코딩 스타일 유지 # ### 2-2 매핑 자료형의 연산자 중복 # In[11]: class MyDict: def __init__(self, d = None): if d == None: d = {} self.d = d def __getitem__(self, k): #key return self.d[k] def __setitem__(self, k, v): self.d[k] = v def __len__(self): return len(self.d) m = MyDict() #__init__호출 m['day'] = 'light' #__setitem__호출 m['night'] = 'darkness' #__setitem__호출 print m print m['day'] #__getitem__호출 print m['night'] #__getitem__호출 print len(m) #__len__호출 # In[12]: class MyDict: def __init__(self, d=None): if d == None: d = {} self.d = d def __getitem__(self, k): return self.d[k] def __setitem__(self, k, v): self.d[k] = v def __len__(self): return len(self.d) def keys(self): return self.d.keys() def values(self): return self.d.values() def items(self): return self.d.items() m = MyDict({'one':1, 'two':2, 'three':3}) print m.keys() print m.values() print m.items() # *** # ## 3 문자열 변환과 호출 가능 객체 # *** # ### 3-1 문자열로 변환하기 # 1) \_\_repr\_\_ # - 객체를 대표하여 유일하게 표현할 수 있는 공식적인 문자열 # - eval() 함수에 의하여 같은 객체로 재생성 될 수 있는 문자열 표현 # # 2) \_\_str\_\_ # - 객체의 비공식적인 문자열 표현 # - 사용자가 보기 편한 형태로 자유롭게 표현될 수 있음 # In[13]: class StringRepr: def __repr__(self): return 'repr called' def __str__(self): return 'str called' s = StringRepr() print s print str(s) print repr(s) print `s` # - \_\_str\_\_() 호출시 # - \_\_str\_\_()가 정의되어 있지 않으면 \_\_repr\_\_()이 대신 호출됨 # In[14]: class StringRepr: def __repr__(self): return 'repr called' s = StringRepr() print s print repr(s) print str(s) print `s` # - \_\_repr\_\_() 호출시 # - \_\_repr\_\_()이 정의되어 있지 않으면 객체 식별자가 출력됨 # - 대신하여 \_\_str\_\_()이 호출되지 않음 # In[15]: class StringRepr: def __str__(self): return 'str called' s = StringRepr() print s print repr(s) print str(s) print `s` # ### 3-2 호출 가능한 클래스 인스턴스 만들기 # - 클래스 인스턴스에 \_\_call\_\_ 메소드가 구현되어 있다면 해당 인스턴스는 함수와 같이 호출될 수 있다. # - 인스턴스 x에 대해 x(a1, a2, a3)와 같이 호출된다면 x.\_\_call\_\_(a1, a2, a3)가 호출된다. # In[16]: class Accumulator: def __init__(self): self.sum = 0 def __call__(self, *args): self.sum += sum(args) return self.sum acc = Accumulator() print acc(1,2,3,4,5) print acc(6) print acc(7,8,9) print acc.sum # In[17]: class Accumulator: def __init__(self): self.sum = 0 def __call__(self, *args): self.sum = reduce(lambda x, y: x + y, args) return self.sum acc = Accumulator() print acc(1,2,3,4,5) print acc(6) print acc(7,8,9) print acc.sum # - 호출 가능 객체인지 알아보기 # In[18]: def check(obj): if callable(obj): print 'callable' else: print 'not callable' class B: def func(self, v): return v class A: def __call__(self, v): return v a = A() b = B() check(a) check(b) print print callable(a) print callable(b) #

참고 문헌: 파이썬(열혈강의)(개정판 VER.2), 이강성, FreeLec, 2005년 8월 29일