본문 바로가기
Computer Technology 기록부/코딩기록부 : Python

객체지향프로그래밍(OPP)이란?

by Amins 2022. 1. 10.

파이썬은 객체지향 프로그램이라고 불린다. 여기서 객체는 무엇이고, 객체지향 프로그래밍은 무엇을 의미할까?

 

객체(Object)란 무엇인가?

변수에 할당되거나 인자로 넘겨질 수 있는 모든 것들, 파이썬의 경우 모든 것이 객체

 

b라는 함수에 4라는 데이터 할당

- b4를 할당하면, b4의 주소는 동일

[In] : 

print(id(var), id(4)) #id 값은 pc마다 다릅니다.

[Out] :

140655556567744 140655745104272

 


얕은 복사 vs 깊은복사

얕은 복사 원본 데이터의 주소 값 만 복사

깊은 복사 원본 데이터의 값을 복사


Class의 기원

A라는 자동차와 B라는 자동차를 각각의 변수로 만들고자 한다면 car_a_tire, car_b_tire, car_a_window, car_b_window, ... 처럼 많은 변수들을 같은 계층에서 만들어야 함.. 함수 역시 car_a_run(), car_b_run(), car_a_stop(), car_b_stop(), ... 처럼 계속 늘어나야 해 굉장히 비효율적임

그러나, 만약 Car라는 묶음을 만들어 놓고 Car 묶음 안에 tire를 넣어 놓으면 car_a.tire, car_b.tire처럼 사용할 수 있으니 계층이 달라지고 변수를 무한히 펼쳐 놓지 않아도 된다. 또 함수도 묶음 안에 넣어버리면 car_a.run(), car_b.run()과 같이 사용할 수 있습니다. 그래서 그 묶음의 형식을 클래스(Class)라고 소개하고 실제로 만들어 낸 각각의 묶음(car_a, car_b)을 객체(Object)라고 부르게 된다. 이후로 이점은 많은 컴퓨터 언어에 기본 아이디어가 된다.

 

<참조>

- 함수를 호출하는 것과 클래스로 객체를 만드는 것은 문법이 비슷

- 클래스에서도 상속을 사용할 때는 클래스를 선언 시 소괄호를 붙이기도 함


클래스 사용 1. - 클래스 선언

class 키워드 사용해서 클래스 선언
클래스 기본구조

예시

class Car:
    pass

class Car():
    pass

#id(Car)는 여러번 호출해도 같은 값이 얻어집니다. 
print(id(Car))
print(id(Car))

#id(Car())는 Car()가 호출될 때마다 다른 값이 얻어집니다. 
print(id(Car()))
print(id(Car()))

# 두 객체의 type을 살펴봅니다. 
print(type(Car))
print(type(Car()))

결괏값

94245910171456
94245910171456
140655682884608
140655682862048
<class 'type'>
<class '__main__.Car'>

이처럼 클래스 선언은 class 키워드를 이용합니다. class 키워드를 쓰고 클래스 이름을 쓴 다음 :(콜론)을 씁니다. 클래스 이름 다음에 소괄호를 적어 주어도 되지만 보통은 생략합니다.

 

위 코드에서 보면, Car라는 클래스 자체도 객체이고, Car()를 호출할 때마다 새로운 Car 타입의 객체가 생성됩니다. 둘 다 id가 존재하기 때문입니다. 그러나, id(Car)의 값은 몇 번 호출되더라도 동일하지만 id(Car())는 호출될 때마다 다른 id 값을 가진다는 것도 알 수 있습니다.

 

Car라는 클래스는 type 유형의 객체입니다. Car()가 호출될 때 Car 타입의 객체가 생성됩니다.


클래스 사용 2. - 객체  인스턴스화

인스턴스화? : 선언한 클래스 이름에 괄호를 적어 변수에 할당

mycar = Car()
mycar2 = Car()
print(id(mycar))
print(id(mycar2))

결괏값

140655556619904
140655556616448

해당 코드에선 Car() 가 인스턴스

인스턴스가 생성될 때마다 객체를 할당받은 변수들에게(여기서는 mycar, mycarr2) 각각 다른 id가 부여됩니다.

 

[참조] 클래스 vs 함수 표기 차이

클래스각 단어의 앞 글자를 대문자로 쓸 것.

  • 예시: mycar —> MyCar
  • 주로 명사로 표기

함수단어는 소문자로 쓰고 각 단어의 연결은 언더바(_)를 사용할 것

  • 예시: mycar —> my_car
  • 주로 동사로 표기
다른 사람이 만들어놓은 경우에는 type() 함수를 이용해서 확인해 보는 것도 좋은 방법!

클래스의 속성과 메서드

 

  • 클래스의 속성은 상태(state)를 표현합니다. 속성은 변수로 나타냅니다 color = 'red'

 

  • 클래스의 메서드는 동작(behavior)을 표현합니다. 메서드는 def 키워드로 나타냅니다. 동작이라고도 합니다

클래스 Car 정의해보기

class Car:
    '''
    속성은 클래스의 상태를 나타냅니다.
    색상: 빨강
    종류: 스포츠 카
    '''
    color = 'red'
    category = 'sports car'
		
    '''
    동작은 메서드로 나타냅니다.
    '''
    def drive(self):
        '''
        주행 메서드
        '''
        print("I'm driving")
				
    def accel(self, speed_up, current_speed=10):
        '''
        가속 메서드
        :param speed_up: 현재속도
        :param current_speed: 가속
        :type speed_up: string
        :type current_speed: string
        '''
        self.speed_up = speed_up
        self.current_speed = current_speed + speed_up
        print("speed up", self.speed_up, "driving at", self.current_speed)
  • 속성
    • 클래스의 상태는 속성이라고 부르며, 변수로 선언합니다.
    • color에는 'red'를 category에는 'sports car'를 할당합니다.
  • 메서드
    • 동작은 메서드로 나타내고 메서드 선언은 함수와 똑같이 def 키워드를 이용합니다.
    • 클래스의 메서드는 첫 번째 인자는 self 값을 적어 주어야 합니다. (self 에 대한 설명은 나중에 하겠습니다)
    • 주행은 drive() 라는 메서드명으로 정의하고 self외에 인자는 없습니다.
    • 가속은 accel() 라는 메서드명으로 정의하고 현재 속도와 가속할 값을 인자로 받습니다.
      • 현재속도 인자명 = current_speed, 가속 인자명 = speed_up
      • accel 메서드에 self.speed_up, self.current_speed 를 인자로 줍니다.

메서드를 호출하는 방법은 속성 접근과 유사합니다. 인스턴스 객체에. 을 쓰고 그 뒤에 메서드 명을 적어줍니다.
메서드는 def 키워드로 구현한다고 했습니다. 다시 말하면 클래스의 메서드는 클래스 내에 정의된 함수라고 말할 수 있습니다.
따라서, 함수를 호출하는 것과 동일하게 함수 이름을 쓰고 소괄호 "()"를 적어 줍니다.


Car 클래스 사용해보기

 
mycar = Car()

print(mycar.color)
mycar.drive()
mycar.accel(5)


mycar.price #클래스에 없는 메서드를 호출하면 오류발생

결괏값

red
I'm driving
speed up 5 driving at 15

mycar.drive()와 Car.drive(mycar)는 동일!

  • mycar =Car()로 인스턴스화 후 변수할당 했기때문
코드 도식화
self. 인자를 이용한 구현

Self 인자를 통해 함수 뒤에 자기 자신이 인자로 들어갈 수 있게 됨!

 

※ 클래스 메서드 정의 시, self인자 사용 안 하면 에러 발생

class Test:
    def run1(self):
        print("run1")

    def run2():
        print("run2")

t = Test()

t.run1()
t.run2() #d여기서 오류발생 - TypeError: run2() takes 0 positional arguments but 1 was given

접두사 self.

인스턴스의 속성으로 사용하고 싶은 변수는 self.을 써준다. self 인자를 통해 선언된 객체의 값이란 의미

객체 안에서 self를 사용하면 인스턴스 객체의 고유한 속성을 나타낼 수 있습니다. 클래스가 아닌 self, 즉 인스턴스화 된 객체 자신의 속성


참고로 클래스의 메서드 내부에서 self. 접두사가 없이 일반 변수와 같게 선언된 변수는 메서드 내부에서만 사용되므로, self.를 사용해 참조할 수 없다

class Test2:
    def run1(self, a):
        self.a = float(a) * 10
        print(self.a)

    def run2(self, b):
        b = float(b) + 10
        print(self.b)
        
t = Test2()

결괏값

t.run1(1) #정상작동

t.run2(1) #AttributeError: 'Test2' object has no attribute 'b'가 발생

 

요약

  • self는 자기 자신입니다.
  • 클래스에 의해 생성된 객체(인스턴스)를 가리킵니다.
  • 클래스의 메서드는 인자로 해당 인스턴스(self)를 받아야 합니다.
  • 메서드를 호출할 때는 self 인자를 전달하지 않습니다. self의 값은 인터프리터가 제공합니다.
  • 인스턴스 변수를 정의할 때에는 접두사 self.을 붙여줍니다. (인스턴스에서 값을 받아 사용되는 변수)

생성자 __init__

클래스에 의해 생성된 인스턴스 객체의 '속성'값을, 인스턴트 생성 시마다 초기화하고 싶을 때 사용

 

 

<초기화 없이 미리 지정한 속성 값으로 고정된 클래스>

class Car:
    color = 'red'
    category = 'sports car'

    def drive(self):
        print("I'm driving")

    def accel(self, speed_up, current_speed=10):
        self.speed_up = speed_up
        self.current_speed = current_speed + self.speed_up
        print("speed up", self.speed_up, "driving at", self.current_speed)

 

<초기화 함수를 이용해 속성 값이 가변적인 클래스>

class Car2:
    def __init__(self, color, category):
        self.color = color
        self.category = category

    def drive(self):
        print("I'm driving")

    def accel(self, speed_up, current_speed=10):
        self.speed_up = speed_up
        self.current_speed = current_speed + self.speed_up
        print("speed up", self.speed_up, "driving at", self.current_speed)
  • __init__ 메서드 안에 인자를 전달함으로써 인스턴스 객체의 속성을 초기화할 수 있습니다.
  • 즉, __init__ 메서드 안에 정의된 속성(변수) color와 category는 클래스를 인스턴스화할 때 값을 설정할 수 있습니다.
  • 이를 인스턴스 객체의 초기화 (initializing instance) 라고 히고, __init__함수는 생성자(constructor)라고 합니다.
  • __init__ 역시 def 키워드로 정의합니다. 

생성자 요약

  • __init__이라고 쓰고, "던더(Double Under) 이닛"이라고 발음합니다.
  • 파이썬의 생성자는 초기화만 수행합니다. 객체 인스턴스화는 클래스 사용 시 변수 할당을 통해 이루어집니다.
  • __init__처럼 앞뒤에 언더바(_)가 두 개씩 있는 메서드를 매직 메서드 라 부름

클래스 변수 vs 인스턴스 변수

  • 클래스 변수 : 클래스 내에서 값이 할당되어 고정된 변수 (모든 객체에서 같은 값 조회)
  • 인스턴스 변수 : self 인자를 이용해 인스턴스화 된 객체에서 값을 인자로 받는 변수(서로 다른 객체 간 값 공유 X)
class Car:
    Manufacture = "India"                            #클래스변수

    def __init__(self, color, category='sedan'):
        self.color = color                           #인스턴스변수
        self.category = category                     #인스턴스변수

상속

기존 클래스의 기능은 유지한 채, 몇 가지 기능만 추가해 만들고 싶을 때

 

새로운 클래스 선언 시 괄호 안에 '상속받을 클래스' 이름 넣기!

class NewCar(Car):
    maker = 'Porsche'

car = NewCar()
car.maker

결괏값

'Porsche'

기존 Car의 함수도 다 사용 가능하고, 새롭게 추가된 속성도 지님

 

  • 상속받은 클래스를 "자식 클래스", "서브 클래스(sub class)", "파생된 클래스(derived class)"라고 합니다.
  • 기존 클래스를 "부모 클래스", "슈퍼 클래스(super class)", "베이스 클래스(base class)"라고 합니다.

상속 시 가능한 기능들

  • 메서드 추가하기(add)
class NewCar(Car):
    def fly(self):
        print("I'm flying!! This is the new car!!")

기존 클래스의 메서드들과 함께 새 메서드도 사용 가능

 

  • 메서드 재정의하기(override)
class NewCar(Car):
    def fly(self):
        print("I'm flying!! This is the new car!!")

    def drive(self):
        print("I'm driving and can fly")

기존에 있는 메서드 변경도 가능(덮어쓰기 개념)

 

  • (중요)부모 메서드 호출하기(super()) -주로 부모 클래스의 속성세팅을 상속받고 싶을때 사용

<형식>

def (부모클래스의)메서드이름():
	super().메서드이름()

<사용 방식, 목적>

class NewCar(Car):
    def __init__(self, color, category, maker):
        super().__init__(color, category) #여기주목
        self.maker = maker

이 경우, color와 category 클래스 변수의 경우 부모 클래스에서의 변경만으로 자식 클래스의 변경이 함께 됨

 

<이렇게 하지 않는 이유>  

class NewCar(Car):
    def __init__(self, color, category, maker):
        self.color = color
        self.category = category
        self.maker = maker

위와 같이 하면 color 값과 같은 하나의 클래스 속성의 초깃값을 전부 변경하고 싶을 땐 모든 자식 클래스들의 코드에서 color 초깃값을 변경해줘야 합니다. 왜냐하면, color값은 NewCar 내의 생성자로 인해 인스턴스 생성 시마다 초기화되기 때문입니다. 이 경우 상속을 받더라도, 부모 클래스의 color값은 받을 수 없습니다.

 

그러나 super() 메서드를 써서 부모 클래스의 속성을 상속받으면 부모 클래스의 속성만 바꿔도 모든 자식 노드들의 속성이 변합니다.

즉, 부모 클래스에서 color라는 변수 값을 바꾼다면 모든 자식 클래스의 color 변숫값도 따라 변할 수 있습니다.

 

댓글