본문 바로가기

파이썬 기초편(1) - 클래스와 객체 본문

Programming/python

파이썬 기초편(1) - 클래스와 객체

ksoes 2022. 6. 14. 23:05

 

 

● 전역변수와 지역변수

  • 함수 안에 선언되는 변수   <지역변수>
  • 함수 밖에 선언되는 변수   <전역변수>

지역변수의 범주에는 매개변수도 포함이 된다

def func(n):	# 매개변수 n도 지역변수 범주에 포함(지역변수의 일종이다)
    lv = n + 1
	print(lv)

print(lv)		# 함수 밖에서 지역변수 lv에 접근, 따라서 오류!
'''
지역변수는 함수 내에 만들어졌다가 함수를 벗어나면 사라지는 변수이다
'''
cnt = 100		# 함수 밖에서 선언된 전역변수 cnt
cnt += 1		# 전역변수 cnt에 접근
def func():
	print(cnt)	# 함수 내에서 전역변수 cnt에 접근!

func()

함수 내에서는 얼마든지 함수 밖에 선언된 변수에 접근할 수 있다

 

cnt = 100		# 전역변수 cnt 선언
def func():
    cnt = 0		# 이럴 경우엔 "지역변수 cnt를 선언하고 0을 저장한다"
    print(cnt)	# 새로 생성된 지역변수의 값인 0을 출력한다

func()


'''만약! global 키워드가 있다면?'''
cnt = 100
def func1():
    global cnt	# 이 함수 내에서 접근하는 cnt는 "전역변수"임을 알려준다
    cnt = 0	# 전역변수 cnt에 0 저장
    print(cnt)	# 전역변수 cnt의 값인 0출력

func1()

 

객체지향 프로그래밍

  • 객체지향 프로그래밍(OOP; Object Oriented Programming)

OOP와 별개로 클래스와 객체는 좋은 프로그램을 만드는 도구가 된다. 다시 말해 OOP와 상관없이 내게 필요한 클래스를 만들고 또 객체를 만들 수 있어야 한다.

 

클래스와 객체 이전의 프로그램에 대한 반성

fa_age = 39
def up_fa_age():
    global fa_age	# 이 함수에서 접근하는 fa_age는 전역변수임을 선언함!
    fa_age += 1
def get_fa_age():
    return fa_age
    
mo_age = 35
def up_mo_age():
    global mo_age	# 이 함수에서 접근하는 fa_age는 전역변수임을 선언함!
    mo_age += 1
def get_mo_age():
    return mo_age

def main():
    print("2019년...")
    print("아빠:", get_fa_age())
    print("엄마:", get_mo_age())
    prin("2020년...")
    up_fa_age()
    up_mo_age()
    print("아빠:", get_fa_age())
    print("엄마:", get_mo_age())

함수를 만들어서 전역변수에 접근하는 방식은 직접 전역변수에 접근하지 않기에 코드를 더 안정적이고 이해하기 쉽게 만든다. 하지만 만약 여기에 본인과 동생의 나이까지 관리한다고 생각해보자. 코드양이 배로 늘어날 것이다. 이러한 문제점을 클래스와 객체를 통해서 해결해보자

 

클래스와 객체의 이해

  • '객체(object)'란 우리 주변에 존재하는 모든 사물 하나하나를 뜻한다
  • 이 '객체'들을 만들기 위해서는 '설계도'가  필요하다. 그리고 '설계도'가 있다면 이를 기반으로 얼마든지 똑같은 모습의 객체를 만들 수 있는데 바로 이 설계도를 가리켜 "클래스"라 한다.

파이썬 기반으로 우리가 원하는 형태의 객체를 만들기 위해서는 먼저 그 객체의 설계도에 해당하는 '클래스'라는 것을 만들어야 한다. 다시 말해서 클래스를 '정의'해야 한다.

  • "객체를 만들려면 그 객체의 설계도에 해당하는 클래스를 먼저 정의해야 한다"

 

위의 코드를 수정하기 위해 클래스를 하나 만들어본다. 나이와 정보관리를 위해 변수와 함수들을 만든다. 이 클래스가 아빠 나이만을 대상으로 하지 않으므로 수정한다

  • fa_age -> age
  • up_fa_age() -> up_age()
  • get_fa_age() -> get_age() 

이것들을 설계도에 포함시키려면 다음과 같이 수정해야 한다

def up_age(self):	# self가 왜 등장했는지 아직 모름, 단 self는 매개변수임!
    self.age +=1	# age가 아니라 self.ag이다, 이렇게 바뀐 이유 아직 모름
def get_age(self):
    return self.age

일단 global 선언은 필요 없다. 클래스 안에 담길 변수 age는 전역변수가 아니기 때문이다. 그리고 self라는 매개변수가 등장했다.

 

자 이 두 함수를 클래스에 담아보겠다. 클래스의 이름은 AgeInfo라 하자! 진짜 그냥 담으면 된다

class AgeInfo:		# 클래스 AgeInfo의 정의
    def up_age(self):	# 클래스 안에 담긴 up_age 함수
    	self.age +=1
    def get_age(self):	# 클래스 안에 담긴 get_age 함수
    	return self.age

근데 age 변수가 클래스 안에 없다. 그런데 파이썬이 알아서 넣어준다(실제와는 차이가 좀 있는 설명이지만 이렇게 이해하자) 

# class_object.py
class AgeInfo:		# 클래스 AgeInfo의 정의
    def up_age(self):
    	self.age += 1
    def get_age(self):
    	return self.age

def main():
    fa = AgeInfo()		# AgeInfo 객체를 생성하고 이를 변수 fa에 저장
    fa.age = 39
    
    print("현재 아빠 나이...")
    print("아빠:", fa.get_age())# get_age 호출할 때 self에 값 전달하지 않음!
    
    print("1년 뒤...")
    fa.up_age()		# up_age 호출할 때 self에 값 전달하지 않음
    print("아빠:", fa.get_age())# get_age 호출할 때 self에 값 전달하지 않음!

main()

 

  • fa = AgeInfo()    # AgeInfo의 객체를 생성하고 이를 변수에 저장

위 문장이 실행되면 AgeInfo라는 클래스(설계도)를 기반으로 객체가 생성되고, 이 객체를 변수 fa에 저장하게 된다.

  • fa.age = 39

파이썬에 객체 fa에 변수 age를 넣어주는 것은 이 문장이 실행될 때이다. 이 때 변수 age가 생성된다(추가설명은 중급편에서). 변수이름에 점을 찍는 것은, 해당 변수에 저장된 객체의 변수나 함수에 접근하는 행위이다.

 

이제 객체 내부의 상태는 이러하다

  • age = 39
  • def up_age(): age += 1
  • def get_age(): return age

객체 안에 존재하는 변수 age는 어떤 종류의 변수일까? 우리는 지역변수와 전역변수를 알고 있다. 그러나 객체 안의 변수는 이 둘과는 부류가 다른 '인스턴스 변수'이다. 더불어 객체 안에 있는 함수는 '메소드'라고 한다. 정확히는 '인스턴스 메소드'이다.

  • 인스턴스 변수  :  인스턴스(객체) 안에 존재하는 '변수'를 뜻하는 말
  • 인스턴스 메소드  :  인스턴스(객체) 안에 존재하는 '함수'를 뜻하는 말 

인스턴스는 '객체'의 또 다른 표현이다. 의미적인 차이가 조금 있지만 보통은 동일하게 취급되므로 지금은 신경쓰지 않아도 된다. 객체 생성 부분을 이렇게 말해도 된다

  • "AgeInfo의 인스턴스가 생성되어서 변수 fa에 저장되었다"

 

나이 정보 관리하는 이전 예제의 수정 결과

class AgeInfo:
    def up_age(self):
        self.age+=1
    def get_age(self):
        return self.age

def main():
    fa = AgeInfo()
    ma = AgeInfo()
    me = AgeInfo()

    fa.age = 39
    ma.age = 36
    me.age = 12

    sum = fa.get_age() + ma.get_age() + me.get_age()
    print("가족 나이의 합:", sum)

    fa.up_age()
    ma.up_age()
    me.up_age()
    sum = fa.get_age() + ma.get_age() + me.get_age()
    print("1년 후의 합:", sum)

main()

나이를 입력 후 각 객체 안에 있는 변수 age의 값이 초기화 된다

가족이 하나 더 추가되었다 하더라도 그에 따른 변수나 함수를 추가할 필요는 없다. 그저 객체만 하나 더 생성하면 되는 것이다. 이것이 클래스가 주는 장점이다.

self 너 뭐냐!

위의 사진에 있는 up_age, get_age 함수가 똑같으니 공유할 수 있지 않을까? 의문이 생겼고 그 결과 파이썬은 다음과 같은 모델을 생각해내고 완성한다

둘 이상의 객체들이 두 함수를 공유하는 모습

fa.up_age()		# 변수 fa에 저장된 객체의 up_age 함수 호출
# AgeInfo 클래스의 함수가 실제 저장된 위치로 가서 up_age 함수를 호출하되,
# fa를 인자로 전달하면서 다음과 같은 형태로 호출
up_age(fa)
# 매개변수 self에 fa가 전달되므로 다음과 같은 형태로 함수 호출이 진행된다.
# (fa의 메모리공간에 self라는 이름이 하나 더 붙어서 이 순간 self는 fa가 된다)
def up_age(fa):
    fa.age += 1
def get_age(self):
    return self.age

# AgeInfo 클래스의 두 함수를 직접 호출하는 방법은 다음과 같다
AgeInfo.up_age(...)
AgeInfo.get_age(...)

# 즉 우리가 다음과 같이 문장을 작성하면,
fa.up_age()
# 파이썬은 이 문장을 다음 형태로 바꿔서 함수를 호출한다
AgeInfo.up_age(fa)
# fa메모리공간에 self 이름이 붙어 self로 객체를 접근한게 된다. 때문에 self가 필요하고 어떻게 사용되는지 알 수 있다

 

self 이외의 매개변수를 갖는 메소드들(함수들) 정의해보기

클래스의 인스턴스 메소드에도 얼마든지 매개변수를 추가할 수 있다.

def set_age(self, n):	# 매개변수로 self도 있고 n도 있다
    self.age = n
# 이렇게 정의한 메소드를 호출할 때는 self에는 파이썬이 알아서 전달을 하니, n에만 매개변수만 전달하면 된다

def set_age(self, age):
    self.age = age		# age는 매개변수, self.age는 인스턴스 변수
# '매개변수'와 '인스턴스 변수'의 이름이 같아지지만, 함수 안에서 그냥 age라고 쓰면 매개변수가 되고,
# self.age라고 쓰면 인스턴스 변수가 되기때문에 이름이 같아도 된다

코드

더보기
# family_age4.py
class AgeInfo:
    def up_age(self):
    	self.age += 1
    def get_age(self):
    	return self.age
    def set_age(self, age):
    	self.age = age

def main():
    fa = AgeInfo()
    fa.set_age(39)
    fa.up_age()
    print("1년 후 아빠 나이 :", fa.get_age())

main()

fa.age = 9 처럼 인스턴스 변수에 직접 접근해서 초기화 하는 대신

fa.set_age(39) 처럼 메소드 호출을 통해 인스턴스 변수 초기화를 할 수 있다

 

 생성자

객체 생성 후에는 반드시 다음처럼 "인스턴스 변수의 초기화"를 해줘야 한다.

def main():
    fa = AgeInfo()	# AgeInfo의 객체 생성
    fa.age = 39		# 인스턴스 변수 age의 값을 20으로 초기화

 

객체 안에 존재하는 모든 변수는 초기화를 해야 하며, 이러한 초기화를 객체 생성 후에 바로 하는 것이 일반적. 그래서 객체의 생성과 변수의 초기화를 동시에 진행할 수 있도록 '생성자(constructor)'라는 것을 제공

# ctor1.py
class Const:
    def __init__(self):		# 생성자라 불리는 메소드, 메소드 이름이 __init__이다
        print("new")

def main():
    o1 = Const()
    o2 = Const()

main()

생성자는 객체 생성 시 자동으로 호출이 되는 특징이 있다. 생서아도 다른 메소드들과 마찬가지고 매개변수로 self를 넣어줘야한다.

 

# ctor2.py
class Const:
    def __init__(self, n1, n2):
    	self.n1 = n1		# self.n1은 인스턴스 변수, n1은 매개변수
        self.n2 = n2		# self.n2는 인스턴스 변수, n2는 매개변수
    def show_data(self):
    	print(self.n1, self.n2)

def main():
    o1 = Const(1, 2)	# 생성자에 1과 2를 전달
    o2 = Const(3, 4)	# 생성자에 3과 4를 전달
    o1.show_data()	# 결과: 1 2
    o2.show_data()	# 결과: 3 4

main()

 

family_age4.py 파일 수정하여 생성자 사용한 코드

더보기
# family_ag5.py
class AgeInfo:
    def __init__(self, age):
        self.age = age
    def up_age(self):
    	self.age += 1
    def get_age(self):
    	return self.age

def main():
    fa = AgeInfo(39)	# 객체 생성 후 초기화
    fa.up_age()
    print("나이 출력 :", fa.get_age())

main()

 

 

사실 파이썬의 모든 데이터는(값은) 객체

s = "coffee"
s.upper()	# 이런 함수 호출이 가능하다는 것은 s에 담긴 것이 객체라는 의미!
# 결과: COFFEE

n = 1000
n.bit_length()	# 변수 n에 담긴 정수도 객체라는 증거!
# 결과: 10

f = 3.14
f.is_integer()	# 변수 f에 담긴 실수도 객체라는 증거!
# 결과: False

정수나 실수를 입력하면 파이썬은 이를 객체로 만든다. 따라서 그 객체를 대상으로 인스턴스 메소드를 호출할 수 있는 것이다. 

 

Comments