goodthings4me.tistory.com
효율적 개발로 이끄는 파이썬 실천 기술 - 내장함수와 특수메서드
(참고용으로 사용할 부분만 간략하게 정리)
내장 함수(embeded function)
- 객체 타입 조사 : isinstance(), issubclass(), callable()
- 객체 속성 관련 함수 : hasattr(), getattr(), setattr(), delattr()
- 이터러블 객체 받는 함수 : zip(), sorted(), filter(), map(), all(), any()
# ininstance(), issubclass() - 동적 타입 판정
d = {}
print(isinstance(d, dict))
print(isinstance(d, (int, list, dict))) # 튜플의 여러 클래스에서 동시 비교
print(issubclass(dict, object))
print(issubclass(bool, int))
print(issubclass(bool, (list, dict, int, float)))
[결과]
True
True
True
True
True
# 값을 꺼낼 때 dict 타입인지 체크
def get_value(obj, key):
if not isinstance(obj, dict): # obj가 dict 타입이 아니면
raise ValueError
return obj[key]
from collections import UserDict
# collections.UserDict는 사용자 정의 딕셔너리 객체를 생성하는 클래스
class MyDict(UserDict):
pass
my_dict = MyDict()
my_dict['a'] = 1
print(my_dict['a'])
#get_value(my_dict, 'a') # ValueError: , dict의 서브클래스 아니므로 에러 발생
[결과]
1
# callable() - 객체의 호출 가능 여부 판정
# 함수나 클래스, 메서드 등 ()를 붙여 호출할 수 있는 객체를 '호출 가능 객체(callable object)'라고 함
# 특수 메서드 __call__()을 가진 인스턴스도 ()를 붙여 호출 가능
class Threshold:
def __init__(self, threshold):
self.threshold = threshold
def __call__(self, x):
return self.threshold < x
threshold = Threshold(2) # 인스턴스화 시 임계값 지정
print(threshold(3)) # __call__() 메서드가 호출됨
print(threshold(2))
[결과]
True
False
# hasattr() - 객체의 속성 유무 판정
def is_package(module_or_package):
return hasattr(module_or_package, '__path__') # __path__ 속성 가지는 패키지 여부 판단
import json, os
print(is_package(json)) # json은 패키지임
print(is_package(os)) # os는 패키지 아님
[결과]
True
False
# getattr(), setattr(),delattr() - 객체 속성 조작
class Mutable:
def __init__(self, dic):
for k, v in dic.items():
setattr(self, str(k), v) # key를 인스턴스 변수로 하여 값 셋팅
mu = Mutable({'a': 10, 'b': 20})
print(getattr(mu, 'a'), mu.a) # getattr(mu, 'a') == mu.a
delattr(mu, 'b') # 삭제됨
#print(mu.b) # AttributeError: 'Mutable' object has no attribute 'b'
# getattr()은 인스턴스 메서드도 얻을 수 있다
strT = 'python' # 문자열 객체의 인스턴스
ins_method = getattr(strT, 'upper') # upper() 메서드
print(ins_method())
print(strT.upper())
[결과]
10 10
PYTHON
PYTHON
# zip() - 다수의 이터러블 엘리먼트를 동시에 튜플로 반환(이터레이터 생성 내장함수)
x = [1, 2, 3]
y = [4, 5, 6, 7]
res = zip(x, y) # 작은 쪽에 맞춰 반환
print(type(res), res)
print(list(res)) # 내용 확인 위해 리스트로 변환
# 긴쪽에 맞추기는 itertools.zip_longest() 사용
from itertools import zip_longest
print(list(zip_longest(x,y, fillvalue=0)))
[결과]
<class 'zip'> <zip object at 0x7efd9584d8c0>
[(1, 4), (2, 5), (3, 6)]
[(1, 4), (2, 5), (3, 6), (0, 7)]
# sorted() - 이터러블 엘리먼트 정렬, 새로운 객체 반환, 반환값은 리스트임
# 숫자, 문자가 섞여있는 경우, 인수 key에 인수를 하나만 얻는 함수 지정하여 각 엘리먼트를 비교할 값으로 반환하여 사용
x = ['1', '4', 3, 5, '2']
res = sorted(x, key=lambda v: int(v)) # reverse=True 인수 추가하여 역순 정렬도 가능
print(res)
[결과]
['1', '2', 3, '4', 5]
# filter() - 조건에 맞는 엘리먼트만을 포함한 이터레이터 반환, 인수를 하나만 받는 함수를 첫 번째 인수로 지정
x = (1, 4, 3, 5, 2)
res_fil = filter(lambda i: i > 3, x)
print(type(res_fil), res_fil)
print(list(res_fil))
# filter() 조건에 None 전달 시 참(True)이 되는 객체만 남김
x = (1, 0, None, 2, [], 'python')
print(list(filter(None, x))) # filter(None, x)
[결과]
<class 'filter'> <filter object at 0x7efd95216910>
[4, 5]
[1, 2, 'python']
# map() - 이터러블의 모든 엘리먼트에 대해 같은 함수를 적용, 반환 결과는 이터레이터임
x = (1, 4, 3, 5, 2)
res = map(lambda i: i * 10, x)
print(type(res), res)
print(list(res))
# map() 인수로 여러 이터러블 객체를 받기, 첫 번째 인수 함수에 전달하는 인수의 개수와 이터러블 객체의 수는 일치해야 함
k = ('q', 'limit', 'page')
v = ('python', 10, 2)
res = map(lambda i, j: f'{i} = {j}', k, v) # 인수의 개수 일치
print(list(res))
# join()과 조합해 문자열 작성
'?'+'&'.join(map(lambda i, j: f'{i}={j}', k, v))
[결과]
<class 'map'> <map object at 0x7efd94d05d10>
[10, 40, 30, 50, 20]
['q = python', 'limit = 10', 'page = 2']
?q=python&limit=10&page=2
# itemgetter() - sorted()와 조합 시 편리한 모듈 (key를 받고 딕셔너리에서 해당 키(들)에 대응하는 값을 찾아 반환)
from operator import itemgetter
d = {'word': 'python', 'count': 3}
fn = itemgetter('count') # key가 되는 값 받고
print(fn(d)) # 딕셔너리 d에서 찾아 반환, d['conut']를 반환
# sorted()와 조합
counts = [{'word': 'python', 'count': 3},
{'word': 'practice', 'count': 3},
{'word': 'book', 'count': 2}]
res1 = sorted(counts, key=itemgetter('count'))
print(res1)
res2 = sorted(counts, key=itemgetter('count', 'word'))
print(res2)
[결과]
3
[{'word': 'book', 'count': 2}, {'word': 'python', 'count': 3}, {'word': 'practice', 'count': 3}]
[{'word': 'book', 'count': 2}, {'word': 'practice', 'count': 3}, {'word': 'python', 'count': 3}]
# all(), any() - 한 개의 이터러블을 받고, all()은 모든 엘리먼트가 참이면 True, any()는 참인 엘리먼트가 한 개 이상이면 True 반환
print(all([3, '1', 'book']), all([3, 0, 'book']))
print(any([3, None, '']), all([None, 0, {}]))
[결과]
True False
True False
특수 메서드(Special method)
- 파이썬이 암묵적으로 호출하는 특수한 메서드(파이썬의 특징)
- 메서드 이름 앞뒤로 언더스코어 두개(__) 붙음
- 객체를 문자열로 표현 : str(), repr()
- 객체를 논리값으로 평가 : bool()
- 인스턴스를 함수처럼 다룸 : call()
- 속성으로의 동적 접근 : setattr(), delattr(), getattr(), getattribute()
- 이터러블 객체로서 동작 : iter(), next()
- 컨테이너 객체로서 동작 : getitem(), setitem(), contains()
# __str__(), __repr__() - 객체의 이름(변수)를 입력하면 해당 객체의 문자열로 표현, 이 둘은 차이가 있다
# 객체 이름만 입력 시 __repr__() 호출, print()에 인수로 전달 시 __str__() 호출
s = 'string'
s
# 'string'
print(s)
# string
# __repr__()은 디버그 등에 도움이 되는 정보 제공용 특수 메서드
# __str__()은 print(), str(), f'{}' 등에서 사용하는 사용자 친화적 문자열 반환하는 특수 메서드
# __str__()이 없으면 __repr__() 호출됨
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'Point({self.x}, {self.y})'
p = Point(2, 3)
print(p)
[결과]
Point(2, 3)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'({self.x}, {self.y})'
p = Point(2, 3)
print(p)
[결과]
(2, 3)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'({self.x}, {self.y})'
p = Point(2, 3)
p
[결과]
<__main__.Point at 0x7efd8f1b65d0>
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'({self.x}, {self.y})'
def __repr__(self):
return f'Point({self.x}, {self.y})'
p = Point(2, 3)
p
[결과]
Point(2, 3)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f'({self.x}, {self.y})'
def __repr__(self):
return f'Point({self.x}, {self.y})'
p = Point(2, 3)
print(p)
[결과]
(2, 3)
# __call__() - 이 메서드를 구현한 클래스에서는 인스턴스를 함수처럼 호출 가능, 인수턴스 상태 유지 가능(함수와의 차이점)
class Adder:
def __init__(self):
self._values = []
def add(self, x):
self._values.append(x)
def get_values(self):
return sum(self._values)
adder = Adder()
adder.add(10)
adder.add(5)
#adder() # TypeError: 'Adder' object is not callable
adder.get_values() # 결과 보는 메서드 호출
[결과]
15
class Adder:
def __init__(self):
self._values = []
def add(self, x):
self._values.append(x)
def __call__(self):
return sum(self._values)
adder = Adder()
adder.add(10)
adder.add(5)
adder() # 함수처럼 호출
[결과]
15
# 함수 객체의 실체는 __call__()을 구현한 function 클래스의 인스턴스
def fn():
return 1
print(type(fn)) # <class 'function'>
print(hasattr(fn, '__call__')) # '__call__' 속성 존재 확인
dir(fn)
[결과]
<class 'function'>
True
['__annotations__',
'__call__',
'__class__',
'__closure__',
'__code__',
'__defaults__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__get__',
'__getattribute__',
'__globals__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__kwdefaults__',
'__le__',
'__lt__',
'__module__',
'__name__',
'__ne__',
'__new__',
'__qualname__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__']
# __setattr__() - 속성에 대입해 호출하는 특수 메서드, p.x = 1 처럼 __setattr__(self, x, 1) 대입
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __setattr__(self, name, value):
if name not in('x', 'y'):
raise AttributeError('Not allowed')
super().__setattr__(name, value) # 자신의 속성 호출로 무한루프에 빠지는 것 방지
p = Point(2, 3)
print(p.x)
print(p.y)
#p.z = 5 # AttributeError: Not allowed
[결과]
2
3
# __getattr__() - 속성에 접근해 호출
class Point:
pass
p = Point()
p.__dict__ # {}
p.x = 1
p.__dict__ # {'x': 1}
p.y = 2
p.__dict__ # {'x': 1, 'y': 2}
# 인스턴스 이름공간에는 속성 __dict__ 에는 대입된 속성이 저장되어 있다
print(hasattr(p, '__dict__')) # True
dir(p)
[결과]
True
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'x',
'y']
# __delattr__() - 속성의 삭제 호출
class Point:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __delattr__(self, name):
if name in('x', 'y'):
raise AttributeError('Not allowed')
super().__delattr__(name)
p = Point(1, 2, 3)
#del p.x # AttributeError: Not allowed
del p.z
hasattr(p, 'z')
[결과]
False
이터러블
- iter()를 구현한 객체
- iter()의 반환값은 임의의 이터레이터
이터레이터
- iter()와 next()를 구현한 객체
- iter()의 반환값은 자신(self)
# __iter__()
# 이터레이터 객체로 반환, 사용자 정의 클래스에서도 이 메서드를 구현해 이터러블로 사용 가능, for i in x: 에서 for 문은 x의 __iter__()를 호출해 그 반환값(이터레이터 객체)을 사용함
class Iterable:
def __init__(self, num):
self.num = num
def __iter__(self):
return iter(range(self.num)) # range() 반환 이터레이터
for i in Iterable(3): # __iter__() 호출
print(i)
print(iter(range(3)))
[결과]
0
1
2
<range_iterator object at 0x7efd92be97b0>
it = iter(range(10))
print(next(it))
next(it)
[결과]
0
1
# next() - 다음 엘리먼트 반환
class Reverser:
def __init__(self, x):
self.x = x
def __iter__(self):
return self
def __next__(self):
try:
return self.x.pop()
except IndexError:
raise StopIteration()
[val for val in Reverser([3, 1, 6, 4])]
[결과]
[4, 6, 1, 3]
# __getitem__(), __setitem__() - 인덱스와 키를 통해 접근해 호출 (x[1], x['key] 등)
from collections import defaultdict # 초기값은 int 타입 0
class CountDict:
def __init__(self):
self._data = {}
self._get_count = defaultdict(int)
self._set_count = defaultdict(int)
def __getitem__(self, key): # c['x'] 등 참조 시 호출
self._get_count[key] += 1
return self._data[key]
def __setitem__(self, key, value): # c['x'] = 1 등 대입 시 호출
self._set_count[key] += 1
self._data[key] = value
@property
def count(self):
return{
'set': list(self._set_count.items()),
'get': list(self._get_count.items()),
}
c = CountDict()
c['x'] = 10
c['x'] = 20
c['y'] = 30
c.count
[결과]
{'get': [], 'set': [('x', 2), ('y', 1)]}
[참고/출처] 효율적 개발로 이끄는 파이썬 실천 기술 - 파이썬에 숨겨진 힘을 이용해 개발 효율을 높이자!
'코딩 연습 > 코딩배우기' 카테고리의 다른 글
[Python] 문자열 내 특수문자 제거 - replace(), isalnum(), join() 등 사용 (0) | 2021.06.23 |
---|---|
[Python] 폴더(디렉토리) 만들기 - mkdir() or makedirs() (0) | 2021.06.22 |
파이썬 실천 기술 #05 - 이름공간, 스코프 (0) | 2021.06.03 |
파이썬 실천 기술 #04 - 클래스와 인스턴스 (1) | 2021.05.26 |
파이썬 실천 기술 #03 - 함수 (0) | 2021.05.25 |
댓글