#!/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일