# Everything in Python is an object
2. 함수: Functions are first-class object
일반적인 함수를 통해 함수는 어떤 형태로 정의되는지 살펴보도록 하겠습니다.
>>>> def area(h, v):
>>>> return h*v
>>>> print(type(area))
<class 'function'>
일반적인 함수를 이야기하면 함수가 클래스 ‘function’ 에 정의되어 있음을 알 수 있습니다. 파이썬은 함수를 정의할 때 사용자가 정의한 함수 객체를 정의합니다. 이를 “Functions are first-class object” 라 이야기합니다. 다음 예시들을 통해 함수 객체를 어떻게 사용할 수 있는지 보겠습니다.
>>>> def yell(text):
>>>> return text.upper() + ‘!’ >>>> yell(‘hello’)
‘HELLO!’
>>>> bark = yell #함수를 다른 변수에 할당
>>>> bark(‘woof)
‘WOOF!’
>>>> del yell # 함수 객체와 변수명은 다른 개념
>>>> yell(‘hello?’)
NameError: name ‘yell’ is not defined
>>>> bark(‘hey’)
‘HEY!’
함수 yell 을 정의하였다면 이는 객체 yell 이 생성된 것입니다. yell 함수가 객체이기 때문에 yell 을 다른 변수에 할당할 수 있습니다. bark=yell 은 함수를 호출하지 않고 yell 과 연결되어있는 함수 객체를 가져와 그 객체를 가리키는 새로운 변수인 bark 를 만들었다고 할 수 있습니다. 이 덕분에 같은 함수를 bark 라는 객체로 호출이 가능합니다.
함수 객체와 변수명이 아예 다른 개념임을 del yell 코드부터 알 수 있습니다. 같은 함수를 가리키는 다른 이름인 bark 라는 객체를 가지고 있기에 원래 이름인 yell 을 지워도 아무 문제 없이 bark 가 잘 실행됨을 알 수 있습니다.
✲ 함수의 메모리 할당
위의 예제들을 통해 파이썬의 함수는 객체이고, 이에 따른 응용 코드들을 보았습니다. 함수의 메모리 할당은 어떻게 일어나는지 살펴보도록 하겠습니다.
# f2
def f2(x):
x=x+1
return x #f1
def f1(x): x=x*2
y = f2(x)
return y
#main
y=5
z = f1(y)
함수를 호출하게 되면 stack 영역에 영역이 생성되며 변수들은 포인터로 작동됩니다. 오른쪽 그림은 호출된 f2 함수가 실행되는 동안 memory 의 작업 상황입니다. f1, f2 함수의 stack 영역이 생성되고 f2 함수가 먼저 실행되어 값에 해당되는 객체들을 가리키는 것을 볼 수 있습니다. 기본적인 자료형을 가지는 객체들로 연결되어있다 보니 연결과 할당이 용이하여 결국 함수도 관리가 용이함을 볼 수 있습니다. 메모리 할당 해제 과정도 같이 보도록 하겠습니다.
왼쪽 그림은 f2 함수가 해제되고 f1 함수의 실행되는 동안의 작업 상황, 오른쪽 그림은 최종 배정 결과입니다. f1, f2 함수가 순서대로 실행이 끝남에 따라 stack memory 에서 사라지며 아무도 가리키지 않는 객체 10 도 사라지는 상황을 볼 수 있습니다. 포인터를 통한 객체 관리가 용이하다 보니 함수를 통한 Garbage collection 에서도 이점을 보이고, 최종적으로 효율적인 메모리 관리를 이끌어낸다는 장점이 있습니다.
3. 클래스
자료형, 함수에 이어 마지막으로 클래스를 구현 어떻게 객체로 작동되는지 알아보겠습니다. 클래스 정의는 앞선 예비 기말프로젝트에서 정의한 클래스를 사용하겠습니다.
class Dog:
#Class Variable
species = "Canis familiaris"
def __init__(self, name, age):
#Instance Variable
self.name = name
self.age = age
self.leg = 4
# Instance method
def description(self):
return f"{self.name} is {self.age} years old"
# Another instance method
def speak(self, sound):
return f"{self.name} says {sound}"
곧바로 객체 정의를 하겠습니다.
>>>> buddy = Dog("Buddy", 9)
>>>> print(type(buddy))
<class '__main__.Dog'>
>>>> print(type(buddy.name))
<class 'str'>
>>>> print(type(buddy.description()))
<class 'str'>
다음과 같이 현재 실행되고 있는 파일에 Dog 라는 클래스를 바탕으로 객체가 생성되며, 객체의 attribute 와 method 를 활용할 때 모두 객체로 실행됨을 알 수 있습니다.
✲ 클래스의 메모리 할당
클래스는 메모리 할당이 어떻게 되는지 다음 예제를 통해 알아보도록 하겠습니다.
class Car:
def __init__(self, w):
self.wheels = w
def getWheels(self):
return self.wheels
# main
c = Car(4)
간단한 Car 클래스 정의와 객체를 생성한 예제입니다. 오른쪽은 클래스 객체를 생성하였을 때 메모리 상황입니다. main 함수에서 c=Car(4) 인 클래스 객체를 생성하기에 생성자 함수 __init__이 호출됩니다. 함수 객체가 생성되므로 Stack memory 영역에 생성됩니다. w 는 4 이고, self 은 Car 인스턴스의 주소값으로 작용합니다. (self 는 항상 자신의 클래스 인스턴스 주소 값으로 작용합니다.) self.wheels 은 객체의 attribute 에 속하므로 Heap memory 에 할당되어 존재합니다. 객체 여러 개를 생성해보겠습니다.
c = Car(4)
c3 = Car(3)
c2 = Car(2)
앞선 코드를 실행해보면 오른쪽 그림과 같이 메모리가 구성됩니다. Heap Memory 에서 객체가 생성되고 이에 따른 매개변수들은 객체들로, 이를 따로 생성하고 포인터로 연결하는 방식으로 이루어집니다.
이처럼 파이썬은 기본적인 자료형부터 시작하여 함수, 클래스 모두 객체로 이루어져 있습니다. 파이썬의 객체 지향 프로그래밍(Object- Oriented Programming) 패러다임은 사물 간의 관계를 모델링하기, 메모리 관리도 용이하며 쉬운 프로그램의 개발로 생산성을 향상시킬 수 있다는 장점을 가지고 있습니다.