TIL16. Python : Function과 내장 매서드

ID짱재·2021년 9월 24일
0

Python

목록 보기
25/39
post-thumbnail

📌 이 포스팅에서는 Python의 First-Class Functions의 특징과 Function의 callable, signatures, partial 매서드에 대해 알아보겠습니다.



🌈 Function과 내장 매서드

🔥 First-Class Functions 특징

🔥 Functions의 parameter

🔥 Functions의 내장 매서드



1. First-Class Functions 특징

🤔 첫째. 함수를 반환하여 재귀적으로 사용 가능

✔️ 재귀함수를 이용하여 Factorial을 이용하면 아래와 같습니다. return을 통해 다시 함수를 반환할 수 있는 것은 Function의 특징 중 하나입니다.

def factorial(n):
    if n == 1: 
        return n
    return n * factorial(n-1) # 👈 함수를 반환 가능
print(factorial(5)) # 120

🤔 둘째. 함수를 변수 등에 할당 가능

✔️ 함수를 변수에 할당한 뒤 사용할 수 있습니다.

def factorial(n):
    if n == 1: 
        return n
    return n * factorial(n-1)
var_func = factorial # 👈 함수를 변수에 할당
print(var_func) # <function factorial at 0x7ffeda7d4040>
print(var_func(5)) # 120
print(map(var_func, range(1,6))) # <map object at 0x7fb245056ee0>
print(list(map(var_func, range(1,6)))) # [1, 2, 6, 24, 120]

🤔 셋째. 다른 함수에 함수를 parameter로 함수를 전달 가능

✔️ map, filter, reduce 등 다른 함수에 함수를 인수로 전달하여 사용할 수 있습니다.

def factorial(n):
    if n == 1: 
        return n
    return n * factorial(n-1)
print(factorial(5)) # 120
print(map(var_func, filter(lambda x: x%2, range(1,6)))) # <map object at 0x7fea8b156fa0> # 👈 map함수에 var_func 함수를 parameter로 전달
print(list(map(var_func, filter(lambda x: x%2, range(1,6))))) # [1, 6, 120] <= 1,3,5 만 실행
print([var_func(i) for i in range(1,6) if i % 2]) # [1, 6, 120] <= 1,3,5 만 실행

✔️ reduce 함수는 배열의 원소를 순회하며, 값을 누적시키면서 함수를 실행합니다.
✔️ 단, python에서는 range함수로도 가능하기 때문에 reduce 함수를 잘 사용하진 않습니다.
✔️ 또한 익명함수를 사용할 때는 자신과 타인을 위해 가급적 주석을 사용하는 편이 좋습니다.

from functools import reduce
from operator import add
# reduce : 함수와 반복되는 객체를 전달 받아 함수에서 요구하되는 기능을 순차적으로 누적시킴
print(reduce(add, range(1,11))) # 55 
print(reduce(lambda x, t: x+t, range(1,11))) # 55 👈 sum을 익명함수로 대체
print(sum(range(1,11))) # 55


2. Functions의 parameter

🤔 함수의 parameter : *args, **kwargs

✔️ 아스타 1개(*args)는 여러 개를 패킹해서 tuple형식으로 넘기고, 아스타 2개(**kwargs)는 dict로 parameter를 패킹하여 전달합니다.
✔️ 아스타 뒤에 args, kwargs는 다른 이름으로 사용해도되지만, 일반적으로 *args, **kwargs를 사용합니다.

def args_test(name, *contents, point=None, **attrs):
    return '<args_test> -> ({}) ({}) ({}) ({})'.format(name, contents, point, attrs)
print(args_test('test1'))
# <args_test> -> (test1) (()) (None) ({})
print(args_test('test1', 'test2'))
# <args_test> -> (test1) (('test2',)) (None) ({})
print(args_test('test1', 'test2', 'test3', id='admin'))
# <args_test> -> (test1) (('test2', 'test3')) (None) ({'id': 'admin'})
print(args_test('test1', 'test2', 'test3', point=7))
# <args_test> -> (test1) (('test2', 'test3')) (7) ({})
print(args_test('test1', 'test2', 'test3', point=7, password='7777'))
# <args_test> -> (test1) (('test2', 'test3')) (7) ({'password': '7777'})


3. Functions의 내장 매서드

🤔 함수와 클래스의 내장 매서드 비교

✔️ 함수에서만 존재하는 매서드는 아래와 같이 확인할 수 있습니다.

  • __name__ : 함수 이름 반환하는 매직 매서드
  • __code__ : 함수의 위치 반환하는 매직 매서드
# 함수
def my_func(n):
    return n
# 클래스
class MyClass:
    pass
print(type(my_func), type(MyClass)) # <class 'function'>, <class 'type'>
print(set(dir(my_func))-set(dir(MyClass))) # 👈 함수만 갖고 있는 속성들 확인
"""
{
'__kwdefaults__', '__name__', '__annotations__', '__get__', '__code__',
'__call__', '__globals__', '__qualname__', '__closure__', '__defaults__'}
"""
print(my_func.__name__) # my_func
print(my_func.__code__) # my_func<code object my_func at 0x7fc32a24b920, file "/Users/jangjaewon/Documents/main.py", line 12>

🤔 callable 매서드란?

✔️ callable은 호출 연산자로 해당 객체가 함수 형태로 호출 가능한지에 대해 Bool 형태로 반환시켜줍니다.
✔️ callable에 객체를 전달했을 때, True이면 함수처럼 호출하여 사용 가능하고 False이면 불가 합니다.

print(callable(str)) # True
print(callable(int)) # True 
print(callable(list)) # True 
print(callable(factorial)) # True 
print(callable(3.14)) # False

✔️ callable 함수를 활용하여 클래스로 생성한 intance를 함수처럼 사용하는 방법을 알아보겠습니다.
✔️ 로또 번호 6개를 랜덤으로 출력해주는 클래스를 만들었을 때, callable 함수로 확인해보면 False를 반환하고 함수처럼 ()를 붙여 호출하면, TypeError가 발생됩니다.
✔️ 이는 클래스로 만든 인스턴스는 기본적으로 함수처럼 호출이 불가능하단 것을 의미합니다.

import random
class LottoGame:
    def __init__(self):
        self._balls = [n for n in range(1, 46)]   
    def pick(self):
        random.shuffle(self._balls)
        return sorted([random.choice(self._balls) for n in range(6)])
game = LottoGame() # 👈 객체 생성
print(game.pick()) #  👈 [10, 14, 14, 20, 32, 35] <=실행할 때마다 랜덤으로 6개씩 뽑아 정렬 후 반환   
print(callable(game)) #  👈 False <= 함수처럼 호출 불가능
# print(game()) #  👈 TypeError: 'LottoGame' object is not callable

✔️ 이럴 때, callable 매직 매서드를 오버라이딩시키면 클래스로 만든 인스턴스를 함수처럼 호출하여 사용할 수 있습니다.
✔️ 함수의 호출 방법인 ()는 매직매서드인 callable과 같습니다.

import random
class LottoGame:
    def __init__(self):
        self._balls = [n for n in range(1, 46)]   
    def pick(self):
        random.shuffle(self._balls)
        return sorted([random.choice(self._balls) for n in range(6)])
    def __call__(self): # 👈 callable 매직 매서드 오버라이딩
        return self.pick()        
game = LottoGame() #  👈 객체 생성
print(game.pick()) #  👈 [10, 14, 14, 20, 32, 35] <=실행할 때마다 랜덤으로 6개씩 뽑아 정렬해서 반환   
print(callable(game)) #  👈True
print(game()) #  👈 [4, 11, 12, 34, 40, 45] <= 함수처럼 사용 가능하기 때문에 인스턴스로 바로 실행 가능

🤔 signatures 매서드란?

✔️ signatures 매서드에 함수를 넣으면 함수의 parameter를 조사(inspect)해서 반환합니다.
✔️ signature 함수는 inspect에서 import하여 사용할 수 있습니다. ⇢ 🔎 from inspect import signature
✔️ parameters을 찍어주면, 더 자세한 정보를 확인할 수 있습니다.

from inspect import signature # 👈 signature 위치
def args_test(name, *contents, point=None, **attrs):
    return '<args_test> -> ({}) ({}) ({}) ({})'.format(name, contents, point, attrs)
sg = signature(args_test)
print(sg) # 👈 (name, *contents, point=None, **attrs)
print(sg.parameters) # 👈 parameters는 더 구체적인 세부사항을 아래처럼 반환시켜줍니다.
"""
OrderedDict([('name', <Parameter "name">), ('contents', <Parameter "*contents">),
('point', <Parameter "point=None">), ('attrs', <Parameter "**attrs">)])
"""
for name, param in sg.parameters.items():
    print(name, param.kind, param.default) # 👈 name, param 종류, param 기본값
"""    
name POSITIONAL_OR_KEYWORD <class 'inspect._empty'>
contents VAR_POSITIONAL <class 'inspect._empty'>
point KEYWORD_ONLY None
attrs VAR_KEYWORD <class 'inspect._empty'>  
"""

🤔 partial 매서드란?

✔️ partial 매서드는 인수(parameter)를 고정하여 사용할 수 있게 해주며, 주로 콜백 함수에 사용합니다.
✔️ 첫번째 parameter는 함수를 받고, 두번째 parameter는 고정할 인수를 받습니다.
✔️ 이를 이용하여 하나 이상의 인수(parameter)를 고정(할당)시킨 새로운 함수를 반환받아 사용할 수 있습니다.

from operator import mul # 👈 곱하기 함수
from functools import partial # 👈 partial 함수
print(mul(10, 100)) # 1000 <= mul 함수는 2개의 인자를 받아 곱해주는 "*"와 같습니다.
five = partial(mul, 5) # mul은 파라미터를 2개 받을 수 있는데, 그 중 하나를 5로 고정하겠다.
six = partial(five, 6) # # 이미 인수로 5가 고정된 five 함수에 인수 6을 고정시키겠다.(인수 더이상 못들어감)
print(five(100)) # 500
print(six()) # 30
# 활용
print([five(i) for i in range(1,11)]) # [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
print(list(map(five, range(1,11)))) # [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
profile
Keep Going, Keep Coding!

0개의 댓글