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

버전

메소드(Method)

연산자(Operator)

인스턴스 o에 대한 사용 예

Python2.x/3.x

__add__(self, B)

+ (이항)

o + B, o += B

Python2.x/3.x

__sub__(self, B)

- (이항)

o - B, o -= B

Python2.x/3.x

__mul__(self, B)

*

o * B, o *= B

Python2.x

__div__(self, B)

/

o / B, o /= B

Python3.x

__truediv__(self, B)

/

o / B, o /= B

Python2.x

__rdiv__(self, B)

/

B / o

Python3.x

__rtruediv__(self, B)

/

B / o

Python2.x/3.x

__floordiv__(self, B)

//

o // B, o //= B

Python2.x/3.x

__mod__(self, B)

%

o % B, o %= B

Python2.x/3.x

__divmod__(self, B)

divmod()

divmod(o, B)

Python2.x/3.x

__pow__(self, B)

pow(), **

pow(o, B), o ** B

Python2.x/3.x

__lshift__(self, B)

<<

o << B, o <<= B

Python2.x/3.x

__rshift__(self, B)

>>

o >> B, o >>= B

Python2.x/3.x

__and__(self, B)

&

o & B, o &= B

Python2.x/3.x

__xor__(self, B)

^

o ^ B, o ^= B

Python2.x/3.x

__or__(self, B) 

|

o | B, o |= B

Python2.x/3.x

__neg__(self)

- (단항)

-A

Python2.x/3.x

__abs__(self)

abs()

abs(o)

Python2.x/3.x

__pos__(self)

+ (단항)

+o

Python2.x/3.x

__invert__(self)

~

~o

# In[3]: class MyInteger: def __init__(self, i): self.i = i def __str__(self): return str(self.i) def __add__(self, other): return self.i + other def __sub__(self, other): return self.i - other def __mul__(self, other): return self.i * other i = MyInteger(10) print(i) print(str(i)) print() i = i + 10 print(i) print() i += 10 print(i) print() i += 15 print(i) print() i *= 10 print(i) # - [주의] 위 코드는 연산자 오버로딩을 올바로 설명하지 못하는 코드. 아래 코드가 올바른 코드임. 이유는? # In[5]: class MyInteger2: def __init__(self, i): self.i = i def __str__(self): return str(self.i) def __add__(self, other): return MyInteger2(self.i + other) def __sub__(self, other): return MyInteger2(self.i - other) def __mul__(self, other): return MyInteger2(self.i * other) i = MyInteger2(10) print(i) print(str(i)) print() i = i + 10 print(i) print() i += 10 print(i) print() i += 15 print(i) print() i *= 10 print(i) # In[1]: #python3.x class MyString: def __init__(self, str): self.str = str def __truediv__(self, sep): # 나누기 연산자 /가 사용되었을 때 호출되는 함수 return self.str.split(sep) # 문자열 self.str을 sep를 기준으로 분리 m = MyString("abcd_abcd_abcd") print(m / "_") print(m / "_a") print() print(m.__truediv__("_")) # In[2]: #python2.x 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]: #python3.x class MyString: def __init__(self, str): self.str = str def __truediv__(self, sep): return str.split(self.str, sep) __rtruediv__ = __truediv__ 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 __neg__(self): t = list(self.str) t.reverse() return ''.join(t) __invert__ = __neg__ m = MyString("abcdef") print(-m) print(~m) # In[8]: class MyString: def __init__(self, str): self.str = str def __neg__(self): t = list(self.str) t.reverse() return MyString(''.join(t)) def __str__(self): return self.str __invert__ = __neg__ m = MyString("abcdef") m = -m print(m) m = ~m print(m) # In[10]: class MyString: def __init__(self, str): self.str = str def __floordiv__(self, sep): return self.str.split(sep)[0] def __mod__(self, sep): return self.str.split(sep)[1] def __divmod__(self, sep): seperated_list = self.str.split(sep) return seperated_list[0], seperated_list[-1] m = MyString("aaaa_bbbb_cccc") print(m // "_") print(m % "_") print(divmod(m, "_")) print() print(m.__floordiv__("_")) print(m.__mod__("_")) print(m.__divmod__("_")) # ### 1-2 비교 연산자 중복 # - 각각의 비교 연산에 대응되는 메소드 이름이 정해져 있지만 그러한 메소드가 별도로 정의되어 있지 않으면 cmp가 호출됨 # # |메소드 |연산자 | 비고 | # |--------|----|----| # |\_\_cmp\_\_(self, other) | 아래 메소드가 부재한 상황에 호출되는 메소드| python3.x 에서는 지원하지 않음 | # |\_\_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 | | # In[11]: #python3.x class MyInteger: def __init__(self, i): self.i = i def __gt__(self, y): return self.i > y def __ge__(self, y): return self.i >= y def __lt__(self, y): return self.i < y def __le__(self, y): return self.i <= y def __eq__(self, y): return self.i == y def __ne__(self, y): return self.i != y c = MyInteger(10) print(c > 1) print(c >= 1) print(c < 1) print(c <= 1) print() print(c == 1) print(c != 1) # In[14]: #python3.x class MyInteger: def __init__(self, i): self.i = i def __gt__(self, y): if isinstance(y, MyInteger): return self.i > y.i elif isinstance(y, int): return self.i > y else: return False def __ge__(self, y): if isinstance(y, MyInteger): return self.i >= y.i elif isinstance(y, int): return self.i >= y else: return False def __lt__(self, y): if isinstance(y, MyInteger): return self.i < y.i elif isinstance(y, int): return self.i < y else: return False def __le__(self, y): if isinstance(y, MyInteger): return self.i <= y.i elif isinstance(y, int): return self.i <= y else: return False def __eq__(self, y): if isinstance(y, MyInteger): return self.i == y.i elif isinstance(y, int): return self.i == y else: return False def __ne__(self, y): if isinstance(y, MyInteger): return self.i != y.i elif isinstance(y, int): return self.i != y else: return False c = MyInteger(10) d = MyInteger(100) e = 50 print(c > d) print(c >= d) print() print(c > e) print(c >= e) print() print(c < d) print(c <= d) print() print(c < e) print(c <= e) print() print(c == d) print(c != d) print() print(c == e) print(c != e) # *** # ## 2 시퀀스/매핑 자료형의 연산자 중복 # *** # - 클래스를 개발할 때 다음 메소드들을 적절하게 구현하면 자신만의 시퀀스 자료형을 만들 수 있음 # - 변경불가능한 (Immutable) 시퀀스 자료형 및 매핑 자료형을 위해 구현이 필요한 메소드 # |메소드 |연산자 | # |---|---| # |\_\_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[19]: #python3.x 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("Out of Index - " + str(k)) return k * k s1 = Square(10) print(len(s1)) # s1.__len__() print() print(s1[0]) #s1.__getitem__(0) print(s1[1]) #s1.__getitem__(1) print(s1[4]) #s1.__getitem__(4) print(s1[9]) #s1.__getitem__(9) print(s1[20]) # - 다음 for 문은 s1에 대해 \_\_getitem()\_\_ 메소드를 0부터 호출하여 IndexError가 발생하면 루프를 중단한다. # In[22]: s1 = Square(10) for x in s1: print(x, end=" ") # In[23]: s2 = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] for x in s2: #print(x,) print(x, end=" ") # - \_\_getitem\_\_() 메소드가 정의되어 있다면 다른 시퀀스 자료형으로 변환이 가능 # In[24]: print(list(s1)) print(tuple(s1)) # - 위에서 알 수 있듯이 파이썬은 내장 자료형과 개발자가 정의한 자료형에 대해 일관된 연산 적용이 가능 # - 파이썬 언어의 장점: 일관된 코딩 스타일 유지 # ### 2-2 매핑 자료형의 연산자 중복 # In[26]: 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[28]: 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 list(self.d.keys()) def values(self): return list(self.d.values()) def items(self): return list(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[30]: class StringRepr: def __repr__(self): return 'repr called' def __str__(self): return 'str called' s = StringRepr() print(s) print(str(s)) print(repr(s)) # - \_\_str\_\_() 호출시 # - \_\_str\_\_()가 정의되어 있지 않으면 \_\_repr\_\_()이 대신 호출됨 # In[31]: class StringRepr: def __repr__(self): return 'repr called' s = StringRepr() print(s) print(str(s)) print(repr(s)) # - \_\_repr\_\_() 호출시 # - \_\_repr\_\_()이 정의되어 있지 않으면 객체 식별자가 출력됨 # - 대신하여 \_\_str\_\_()이 호출되지 않음 # In[32]: class StringRepr: def __str__(self): return 'str called' s = StringRepr() print(s) print(str(s)) print(repr(s)) # In[34]: eval('10 + 20') # In[35]: a = '10 + 20' eval(a) # In[37]: b = "print('abc')" eval(b) # In[39]: class StringRepr: def __init__(self, i = 10): self.i = i def __repr__(self): return 'StringRepr(100)' s = StringRepr() q = eval(repr(s)) print(q.i) # ### 3-2 호출 가능한 클래스 인스턴스 만들기 # - 클래스 인스턴스에 \_\_call\_\_ 메소드가 구현되어 있다면 해당 인스턴스는 함수와 같이 호출될 수 있다. # - 인스턴스 x에 대해 x(a1, a2, a3)와 같이 호출된다면 x.\_\_call\_\_(a1, a2, a3)가 호출된다. # In[40]: 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[41]: class A: def __call__(self, v): return v class B: def func(self, v): return v def check(func): if callable(func): print('callable') else: print('not callable') a = A() b = B() check(a) check(b) print() print(callable(a)) print(callable(b)) #

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