-
파이썬 실천 기술 #06 - 내장함수와 특수메서드(스페셜 메서드)코딩 연습/코딩배우기 2021. 6. 12. 00:01반응형
효율적 개발로 이끄는 파이썬 실천 기술 - 내장함수와 특수메서드
(참고용으로 사용할 부분만 간략하게 정리)
내장 함수(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() # 결과 보는 메서드 호출 [결과] 15class 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
