ABOUT ME

IT와 컴퓨터 관련 팁, 파이썬 등과 아파트 정보, 일상적인 경험 등의 생활 정보를 정리해서 올리는 개인 블로그

  • 파이썬 실천 기술 #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

    good4me.co.kr

    # 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)]}

     

    [참고/출처] 효율적 개발로 이끄는 파이썬 실천 기술 - 파이썬에 숨겨진 힘을 이용해 개발 효율을 높이자!

     

    반응형
Designed by goodthings4me.