Day to_day

Object-Oriented Programming (OOP) 본문

Python

Object-Oriented Programming (OOP)

m_inglet 2023. 1. 17. 02:17
728x90
반응형

개발언어로 파이썬을 가장 먼저 배우곤하는데 나 역시 유투브로 파이썬을 배웠었다. 

이후엔 대학에서 기초 파이썬 강의를 한번 수강해본적은 있지만 문법만 배웠었지 자세한 내용은 안배웠던 것 같다. (아님 내가 수업을 제대로 안들었거나)

 

객체지향 프로그래밍이라는 많이 들어봤는데 정확한 개념정리는 해본적이 없는 것 같다. 그저 파이썬은 객체 지향 프로그래밍을 지원한다 뭐다뭐다..

 

그래서 오늘은 객체지향 프로그래밍 (Object-Oriented Programming)에 대해서 정리해보고 개념을 집어 넣어보기로 한다.

 

Object- Oriented Programming (OOP)

객체지향 프로그래밍은 간단하게 말하면 속성행동을 가진 객체를 만들고, 객체들 간의 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다. OOP는 코드 재사용성과 생산성의 향상 효과를 볼 수 있고, 유지 보수의 편의성 역시 장점이다.

 

 

객체 지향 프로그래밍은 이러한 객체 개념을 프로그램으로 표현한다.

속성은 변수(variable), 행동은 함수(method)로 표현된다.

오늘날 다양한 언어(Java, Ruby, Python, C++, Objectivc-C, C#, Kotlin)가 객체지향 프로그래밍을 지원하고, 파이썬도 그 중에 하나이다.

 

 

예를 들어 인공지능 축구 프로그램을 작성한다고 가정하자.

 

객체의 종류 : 팀, 선수, 심판, 공

 

Action :

선수 - 공을 차다, 패스하다

심판 - 휘슬을 불다, 경고를 주도

 

Attribute :

선수 - 선수 이름, 포지션, 소속 팀

팀 - 팀 이름, 팀 연고지, 팀 소속팀

 

이런 식으로 속성과 행동에 대해서 정의할 수 있을 것이다.

이렇게 하면 프로그램을 묶음 단위로 잘개 쪼개서, 추후에 가져다 쓰기 편하다.

 

 

 

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

객체 지향 프로그래밍은 추상화, 캡슐화, 상속, 다형성의 특징을 갖는다. 하나씩 자세히 살펴보자.

 

1. 추상화

추상화는 객체의 공통적인 속성과 기능을 추출하여 정의하는 것을 의미한다.

객체들의 공통된 특징을 파악해 정의해 놓은 코드 설계도라고 생각하면 된다.

큰 특징은 불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 프로그램을 간단하게 만든다.

Class는 object를 포함하는 것의 공통적인 특징을 갖고있어야한다. 그리고 이 Class를 만들 때 너무 자세하게 코드를 짜면 안된다.

추상화란 디테일한 정보를 모두 표현하는 것이 아닌 공통된 특징만을 넣어야하기 때문이다.

 

2. 캡슐화 (다른 말로 Visibility : 노출 설정)

캡슐화는 외부에서 내부데이터를 볼수없게 정보를 은닉하므로써 사용자가 임의로 정보를 수정하는 것을 막는다. 또한 필요 없는 정보에는 접근할 필요가 없으며, 만약 제품으로 판매한다면 소스를 보호할 수 있다.

class Product:
    pass

class Inventory:
    def __init__(self):
        self.items = []
        
    def add_new_item(self, product):
        if type(product) == Product:
            self.items.append(product)
            print("new item added")
        else:
            raise ValueError("Invalid Item")
    
    def get_number_of_items(self):
        return len(self.itmes)

a = Inventory()           # Inventory class 선언
a.add_new_item(Product()) # add_new_item으로 product 추가
>> new item added         # 출력


a.items.append("abd")     # items 리스트에 바로 접근 ("adb" 추가)
print(a.items)
>> [<__main__.Product at 0x1ef71743670>,'abd'] # 출력

# 위와 같이 되면 Product에 아무거나 append이용해서 추가해버릴수가 있음

 

해결 방법 : 언더바 두개 찍어주기


class Inventory:
    def __init__(self):
        self.__items = []
        
    def add_new_item(self, product):
        if type(product) == Product:
            self.__items.append(product)
            print("new item added")
        else:
            raise ValueError("Invalid Item")
    
    def get_number_of_items(self):
        return len(self.__itmes)
    
    @property # 데코레이터를 이용
    def items(self):
        return self.__items
    
    # property 데코레이터를 이용해서 외부에서는 접근이 안되지만 내부에서는 접근할수있게 리턴해줄수있다.
    # 보통 이런 경우 반환을 해줄때 copy를 붙여서 반환한다. 왜냐면 그냥 반환하면 수정하면서 원본까지 수정될수있기 때문

 

 

접근하려고 하면 에러가 난다.

그럼 내가 접근하고 싶을 땐?

# property를 사용해서 접근 가능
print(a.items)
>> [<__main__.Product at 0x1ef71752c70>, <__main__.Product at 0x1ef71743610>]

 

3. 상속 (Inheritnace)

부모 클래스로부터 속성과 method를 받은 자식 클래스를 생성하는 것이다.

class Person: # 괄호에 object라고 해주지않아도 기본값은 object로 설정이 되어있다.
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        
    def about_me(self):
        print("저의 이름은 {0}이구요. 나이는 {1}살 입니다.".format(self.name, self.age))
        
    def __str__(self):
        return "저의 이름은 {0}이구요. 나이는 {1}살 입니다.".format(self.name, self.age)

# Employee가 Person 클래스를 상속받음.
class Employee(Person):
    def __init__(self, name, age, gender, salary, hire_date):
        super().__init__(name, age, gender) 
				# super()은 자기 자신의 부모를 가리킴. self와 비슷한 의미지만 super은 부모
        self.salary = salary
        self.hire_date = hire_date
        
    def do_work(self):
        print("열심히 일을 합니다.")
        
    def about_me(self):
        super().about_me() # 부모 클래스의 함수도 사용 가능
        print("제 급여는 {0}원 이구요. 제 입사일은 {1}입니다.".format(self.salary, self.hire_date))

myPerson = Person("inho", 34, "male")
myPerson.about_me()
# 출력
>> 저의 이름은 inho이구요. 나이는 34살 입니다.

myEmp = Employee("inho", 34, "male", 30000, "2022-05-04") 
# 상속을 받는다고 하는것은 그저 붕어빵틀을 상속받은 것이지. 속재료는 채울것 다 채워야함.
# Class Employee의 필수 인스턴스를 다 채워야 한다는 말이다. 

myEmp.about_me()
# 출력
>> 저의 이름은 inho이구요. 나이는 34살 입니다.
제 급여는 30000원 이구요. 제 입사일은 2022-05-04입니다.

 

 

4. 다형성 (Polymorphism)

같은 이름 메소드의 내부 로직을 다르게 작성하여 기능적으로 같은 일을 하지만 세부적으로 다르게 동작하도록 한다.

예를 들어 draw라는 클래스를 만들어 선을 그리는 기능을 만들었다고 하자. 이때 draw(requtangle), draw(circle)은 기능적으로 선을 그리는 같은 일을 하지만 사각형이냐, 원이냐 동작이 다르게 된다.

class Animal:
    def __init__(self, name):
        self.name = name
    
    def talk(self):
        raise NotImplementedError("Subclass must implement abstract method") 
        # raise는 강제로 에러를 만드는 것.
        # NotImplementedError는 아직 구현되지 않았다는 에러

# Animal class를 상속받아 object로 사용
class Cat(Animal):
    def talk(self):
        return "Meow!"
    
class Dog(Animal):
    def talk(self):
        return "Woof! Woof!"

animals = [Cat('Missy'),
          Cat('meso'),
          Dog('Lassie')]

for animal in animals:
    print(animal.name + ':' + animal.talk()) 
		# 똑같이 talk를 써줬음에도 Cat과 Dog이 다른 값을 리턴하는 것을 볼수있다.

>> Missy:Meow!
meso:Meow!
Lassie:Woof! Woof!

 

 

 

+ 추가) Class 선언하기

class Animal(object):
	def __init__(self, 속성 A, 속성 B, 속성 C): # __는 특수 예약함수나 변수, 함수명 변경으로 사용
		self.속성A = 속성 A
		self.속성B = 속성 B
		self.속성C = 속성 C

	# self.속성에 init함수안에 들어가는 속성을 넣어주는 것이다.
	def __str__(self):
		return "Hello, This is %s" % (self.속성)

class Animal (object):

class : class 예약어

Animal : class 이름

object : 상속 받은 객체 명

 

__init__ : 객체 초기화 예약 함수.

__str__ : string으로 리턴해서 보여주는 함수

*self는 왜 쓰는 걸까?

⇒ 생성된 인스턴스 자신을 의미한다.

⇒ 결국 자기 자신을 가리킬때 쓴다.

 

 

예시

class SoccerPlayer(object): 
	def __init__(self, name: str, position: str, back_number: int):
		# 변수의 타입을 지정해주면서 어떤 데이터 형식이 들어와야되는지를 알려준다.
		self.name = name
		self.position = position
		self.back_number = back_number

	def __str__(self):
		return "Hello, My name is %s. I play in %s" % (self.name, self.position)

	def change_back_number(self, new_number):
		print("선수의 등번호를 변경합니다 : from %d to %d" % (self.back_number, new_number)
		self.back_number = new_number # self.back_number를 new_number로 바꿔주기 
		# 다음번에 self.로 불러올때 적용될 수 있도록

# 객체 생성
John = SoccerPlayer("John", "FW", 14)
'''
각각의 인스턴스가 매핑되어서 선언된다.
'''

# 객체 사용하기
John.change_back_number(2)

'''
밖에서는 John으로 변수명이 쓰지만 함수안으로(SoccerPlayer.change_back_number) 들어가면 
self가 쓰이게 되고 결국 이 self는 함수 안에서는 John을 의미하게 된다.
'''

+몰랐던 사실 : 포멧팅에서 %d 는 정수형, %s는 문자형을 넣는다는 의미

 

728x90
반응형
BIG
Comments