파이썬에서 '함수'란 무엇일까?
파이썬에서 함수(function)는 호출 가능한(callable) 객체입니다. 즉, 호출 연산자인 소괄호 ()를 붙여 실행할 수 있는 대상을 의미합니다. 그런데 흥미롭게도 함수만 호출할 수 있는 것은 아닙니다. 임의의 객체가 __call__ 메서드를 구현하기만 하면, 그 객체 역시 함수처럼 동작하도록 만들 수 있습니다.
def a():
pass # a()는 함수의 대표적인 예시
print(a)
print(type(a))
위 코드를 실행하면 다음과 같은 결과를 확인할 수 있습니다.
<function a at 0x0000000005765C18>
<class 'function'>
메서드는 함수의 특별한 형태
메서드(method)는 함수의 한 가지 특별한 유형으로, 특정 객체(인스턴스)에 묶여 있는지 여부에 따라 바운드 메서드(bound method)와 언바운드 메서드(unbound method)로 나뉩니다.
class C:
def c(self):
pass
print(C.c) # 클래스에서 접근한 메서드 (언바운드 상태)
print(C().c) # 인스턴스에서 접근한 메서드 (바운드 상태)
클래스 자체에서 접근한 메서드는 인스턴스 없이 호출할 수 없습니다. 첫 번째 인자로 반드시 인스턴스를 전달해야 하기 때문입니다. 이를 어기면 다음과 같은 오류가 발생합니다.
Traceback (most recent call last):
File "~.py", line 11, in <module>
TypeError: c() missing 1 required positional argument: 'self'
참고: 파이썬 2에서는 언바운드 메서드가 instancemethod 타입으로 표시되었지만, 파이썬 3부터는 클래스에서 접근한 메서드가 단순히 함수(function)로 취급됩니다.
사실 그 경계는 생각보다 흐릿합니다
파이썬에서는 바운드 메서드, 일반 함수, __call__을 구현한 호출 가능 객체, 그리고 클래스 생성자 사이에 큰 차이가 없습니다. 겉으로 보기에는 서로 다르게 느껴질 수 있지만, 내부적으로는 모두 비슷한 방식으로 동작하며 단지 이름 짓는 관례(naming convention)만 다를 뿐입니다.
이것이 의미하는 바는 명확합니다. 바운드 메서드는 곧 함수처럼 사용할 수 있다는 것입니다. 이는 파이썬을 강력하고 유연하게 만드는 수많은 디테일 중 하나입니다.
>>> d = A().a # A() 인스턴스에 바인딩된 메서드
>>> d() # 이제 d는 일반 함수처럼 호출됩니다
len()과 str()의 차이도 겉으로는 드러나지 않습니다
같은 원리로, len(...)과 str(...)은 본질적으로 다른 존재입니다. str은 타입 생성자(type constructor), 즉 새로운 문자열 객체를 만들어내는 클래스이기 때문입니다. 하지만 사용 방식이 거의 동일하기 때문에 조금 더 깊이 들여다보기 전까지는 그 차이를 눈치채기 어렵습니다.
>>> len
<built-in function len>
>>> str
<class 'str'>
정리
결론적으로 파이썬에서 함수와 메서드 중 무엇이 더 근본적이라고 단정하기는 어렵습니다. 메서드는 함수의 특수한 형태이며, 함수 역시 호출 가능한 객체의 한 종류에 불과합니다. 파이썬의 세계에서는 "그것이 무엇이냐"보다 "호출 가능한가"가 훨씬 중요한 기준이 됩니다. 이러한 통합된 설계 덕분에 우리는 함수, 메서드, 호출 가능 객체를 하나의 문법으로 자유롭게 다룰 수 있습니다.