Python기초(6): 객체지향프로그래밍(OOP), Class, Method, Inheritance

Sophie·2024년 3월 5일
0

Python 기초

목록 보기
6/6

객체지향프로그래밍(Object Oriented Programming)이란?

객체지향프로그래밍은 코드를 구성하는 방법 중 하나이며, 매우 직관적이고 데이터와 코드를 잘 구성해준다. 파이썬 뿐만 아니라 Java, C#, JavaScript, TypeScript, Dart 등등 다양한 언어에서 객체지향프로그래밍을 지원한다.

객제지향 프로그래밍의 특징

1. 클래스(Class)
데이터를 정의하고, 그 데이터를 기반으로 동작하는 함수를 정의하는 것을 도와준다.

2. 메소드(method)
클래스 안에 있는 함수를 말한다. 지금까지 만들었던 함수(function)와 똑같은데 단지 위치의 차이만 존재한다. class에 안에 있으면 method, class 밖에 있으면 function이다.

파이썬은 클래스를 위한 다양한 종류의 메서드를 제공하는데, 그 중에서 init 메서드는 클래스를 생성할 때 자동으로 실행되는 메서드이다.

작성할 때는 def init(): 이렇게 작성하는데, 이때 중요한 점은
method를 가지고 있을 경우 method의 첫번째 argument는 자동으로 self가 된다.
굳이 self라고 명명할 필요는 없지만 보통은 self라고 쓴다고 한다.

class Puppy:

  def __init__(self):
  	  print(self)
      print("Puppy is born!")
      
ruffus = Puppy()

print(ruffus)

위의 예시에 있는 코드를 실행해보면 self와 ruffus 모두 동일한 object를 갖는 것을 알 수 있다. 즉, Puppy라는 class에 있는 모든 method는 init 뿐만 아니라 모든 밑줄을 사용하는 method와 우리가 만든 모든 method에 대해서 첫 번째 argument로 그들 자신을 참조한다는 뜻이다.

아래의 예시와 같이 Puppy라는 클래스를 각기 다른 변수명으로 지정한 후,
클래스 안의 메소드를 호출해보면 self에 대해 좀 더 이해할 수 있다.

class Puppy:

  def __init__(self):
      self.name = "Ruffus"
      self.age = 0.1
      self.breed = "Beagle"

ruffus = Puppy()
bibi = Puppy()

print(
  bibi.name,
  ruffus.name
)

먼저 Puppy의 메소드로 name, age, breed 등을 만들어 놓은 후
각각 ruffus, bibi라는 변수명으로 지정한다.

그런 후에 print함수로 bibi.name, ruffus.name을 호출하면
self자리에 각각 bibi, ruffus가 들어가서 둘 다 Ruffus라는 값이 콘솔창에 뜨게 된다.

여기서 조금 변경하면 메소드에 지정된 값이 아닌, 함수에서 호출한 각각의 값을 불러올 수도 있다.

기존의 메소드는 self라는 자동 argument만 가진 후 메소드 안에 name, age, breed를 아예 고정해뒀기 때문에 클래스를 호출하면 무조건 Ruffus라는 이름의 0.1살, Beagle이 호출된다. 그런데 여기에 name, breed라는 argument를 추가해서 함수를 호출할 때 해당 값을 지정할 수 있도록 변경할 수 있다.

class Puppy:

  def __init__(self, name, breed):
      self.name = name
      self.age = 0.1
      self.breed = breed

ruffus = Puppy("Ruffus", "Beagle")
bibi = Puppy("Bibi", "Golden Retriever")

print(
  bibi.name,
  ruffus.name
)

위의 예시처럼 age 값만 고정하고 name과 breed는 argument에 지정한 이름 그대로 넣어준다. 그런 후에 각각 ruffus, bibi라는 변수명으로 지정한 후 argument로 각 변수명이 원하는 값(Ruffus,Beagle/Bibi,Golden Retriever)을 지정한다.

그런 후에 print 함수로 호출해주면 아까와는 달리 Bibi, Ruffus라는 각기다른 결과값을 볼 수 있다.

2.1 str()

str()메서드는 init()에서 세팅해둔 데이터를 활용해 문자열로 나타낼 때 사용된다.

여기서 중요한 점은 우리가 커스터마이징한대로 결과값을 호출하기 위해서는 변수 그 자체(eg. print(bibi))를 호출해야 한다는 점이다.

만약 아까처럼 print(bibi.name)을 호출하면 init메서드에서 지정한 값이 호출된다.

2.2 __를 사용하지 않는 메서드

파이썬에서 반드시 __를 사용해서 메서드를 만들어야하는 것은 아니다.
woof_woof()라는 함수명을 사용해서 Woof!Woof!라고 프린트되는 메서드를 만들 수 있고

introduce()함수를 만들어서 위에서 만든 woof_woof() 메서드와 init에서 지정한 argument를 동시에 활용할 수도 있다.

3. 상속(Inheritance)

상속은 우리가 만든 코드를 저장하고 다시 사용할 수 있도록 해준다.

예를 들어서 우리가 위에서 만든 Puppy 클래스 외에 GuardDog 이라는 새로운 클래스를 만든다고 할 때, name, breed, age를 또 지정해야하면 Puppy 클래스에서 만든 코드를 또 만들어줘야하는 이중 일이 된다.

이런 경우에 Puppy와 GuardDog이 공통으로 가지는 요소(age, name, breed)를 Dog이라는 클래스를 만들어서 상속받을 수 있도록 해줌으로써 해결할 수 있다.

상속을 해주기 위해서 먼저 Dog이라는 클래스를 만들어준다.

class Dog:
  
  def __init__(self, name, breed, age):
    self.name = name
    self.breed = breed
    self.age = age

그런 후, 해당 클래스를 상속받을 Puppy와 GuardDog 클래스의 argument에 Dog 클래스를 넣어준다.

class GuardDog(Dog):

class Puppy(Dog):

이렇게 해줌으로써 Puppy가 woof_woof도 가지는 동시에 name, breed, age도 가지도록 해줄 수 있다.

그런데 이렇게 실행해주면 Dog에 init이 age라는 필수 argument가 없다고 나온다.
왜냐하면 Puppy와 GuardDog을 초기화 할 때 파이썬은 자동으로 Dog의 init()메소드를 호출하고 있고, 그렇기 때문에 Dog 클래스는 (name, breed, age) 세 개의 argument를 받길 원하는데 Puppy와 GuardDog은 name과 breed만 보내주고 있기 때문이다(기존의 코드에서는 age를 각각 0.1, 5로 고정했기때문).

이를 해결하기 위해서 age=0.1, age=5 이렇게 똑같이 추가해줄 수 도 있지만,
다른 방법으로 Puppy와 GuardDog의 init메소드를 활용할 수도 있다.

먼저, Puppy와 GuardDog의 init메소드를 초기화해주기 위해 super를 사용한다.
여기서 super는 Dog인 부모의 class를 참조하는 걸 말한다.
따라서 super().init()은 Dog의 init메소드를 호출한다는 의미다.

그런 다음 외부에서 입력한 name, breed, 그리고 고정된 0.1을 받기 위해서 super().init(name, breed, 0.1)을 입력해준다.

class Puppy(Dog):
	def __init__(self, name, breed):
    	super().__init__(name, breed, 0.1)

class GuardDog에도 똑같은 과정대로 변경해주는데 나이만 5로 변경해준다.

class GuardDog(Dog):

  def __init__(self, name, breed):
    super().__init__(name, breed, 5)
    self.aggresive = True
    
  def rrrrr(self):
    print("stay away!")

정리하자면, GuardDog을 초기화할 때 definit(self, name, breed)를 통해 외부에서 받아온 name과 breed값을 붙여 넣고, 그것들을 super().init(name, breed, 5)를 통해 Dog의 init() 메소드에 전달해주는 것이다.

이렇게 코드를 작성한 후에 ruffus는 Puppy, bibi는 GuardDog의 Class를 받는다고 해준 후 각각 외부의 입력값인 name과 breed를 입력해준다.

ruffus = Puppy(
  name = "Ruffus",
  breed = "Beagle"
)
bibi = GuardDog(
  name = "Bibi",
  breed = "Chihuahu"
)

그런다음 bibi와 ruffus를 각각 호출해주면 참조한 class 값을 잘 받아오는 것을 알 수 있다.

전체 코드

#ruffus와 bibi 둘 다 영향을 받는 class
class Dog:
  
  def __init__(self, name, breed, age):
    self.name = name
    self.breed = breed
    self.age = age
  
  def sleep(self):
    print("Zzzzzz...")

  def introduce(self):
    print(f"I am {self.name} and I am {self.age} years old")

class GuardDog(Dog):

  def __init__(self, name, breed):
    super().__init__(name, breed, 5)
    
    self.aggresive = True
    
  def rrrrr(self):
    print("stay away!")


class Puppy(Dog):
  #Dog의 init매소드 참조
  def __init__(self, name, breed):
    super().__init__(name, breed, 0.1)
    
    self.spoiled = True
    
  #문장 그대로 나타내주는 매소드
  def __str__(self):
    return f"Puppy named {self.name}, breed:{self.breed}, age:{self.age}"

  def woof_woof(self):
    print("Woof Woof!")

#ruffus는 Puppy 클래스, bibi는 GuardDog 클래스 참조
ruffus = Puppy(
  name = "Ruffus",
  breed = "Beagle"
)
bibi = GuardDog(
  name = "Bibi",
  breed = "Chihuahu"
)


#Puppy 클래스 참조
ruffus.introduce()
#GuardDog 클래스 참조
bibi.rrrrr()
#Dog 클래스 참조(공통)
ruffus.sleep()
bibi.sleep()

0개의 댓글