OOP: 객체 지향 프로그래밍

감자·2024년 4월 16일
0

TIL Python

목록 보기
14/16
post-thumbnail

본 포스팅은 geeksforgeeks-Python OOPs Concepts 페이지를 번역 및 재구성하여 작성한 포스팅입니다.



개념

객체 지향 프로그래밍은 매우 중요한 개념으로 개발자들이 관리하기 쉽고 확장성 있는 어플리케이션을 만들 수 있도록 돕습니다.

객체 지향 프로그래밍의 핵심은 클래스와 객체라는 개념에 기반을 두고 있습니다.

클래스 : 특정 유형의 객체를 만드는 설계도와 같은 역할
객체 : 클래스로 생성된 실체

Python에서 객체 지향 프로그래밍(이하 OOP)를 활용하면 아래의 원칙을 적용할 수 있습니다.

Class in Python (클래스)
Objects in Python (객체)
Polymorphism in Python (다형성)
Encapsulation in Python (캡슐화)
Inheritance in Python (상속)
Data Abstraction in Python (데이터 추상화)


1. Class in Python (클래스)

생성

Python의 Class는 class 키워드를 사용하여 생성합니다. Class의 기본 구조는 아래와 같습니다.

class ClassName:
    # Statement-1
    .
    .
    .
    # Statement-N

예제

아무 기능도 하지 않는 빈 클래스를 생성하려면 아래와 같습니다.

class StudyPyothon:
	pass

이번엔 사람을 소개하는 클래스를 만들어보겠습니다.
이 클래스는 이름과 나이를 속성으로 갖고 인사를 하는 메소드를 갖습니다.

class Preson:
	def __init__(self, name, ago):
    	self.name. = name
        self.ago = ago
       
    def greet(self):
    	print(f"My name is {self.name} and I'm {self.age} years old")
 
 
# 객체 생성 및 사용
person1 = Person("감자", 28)
person1.greet()

2. Objects in Python (객체)

객체는 State, Behavior, 그리고 Identity를 포함하는 개념으로 구성되어 있습니다.

1) Identity(식별성)
객체의 Identity는 그 객체를 다른 객체와 구별할 수 있는 고유한 이름을 의미합니다.
person1이라는 변수 이름은 Person 객체의 Identity로 작용하여 이 객체를 다른 Person 객체와 구분합니다.

2) State(상태) 또는 Attributes(속성)
객체의 상태는 그 객체의 특성을 나타내는 속성들로 구성됩니다.
Person 클래스에서는 nameage 속성이 이에 해당합니다. person1 객체는 이름이 "감자"이고 나이가 28살이라는 State/Attributes를 가집니다.

3) Behavior(행동)
객체의 행동은 그 객체의 메소드에 의해 표현되며, 객체가 다른 객체들과 어떻게 상호 작용하는지를 반영합니다.
Person 클래스에서는 greet 메소드가 객체의 Behavior을 정의합니다.


+) self 키워드와 __init__ 메소드

위의 예시 코드를 봤을 때 self, __init__가 뭔지 궁금해하신 분들도 계실텐데요,
지금부터 상세히 알려드리도록 하겠습니다.

self 키워드

self는 클래스의 인스턴스 자신을 의미합니다. 클래스의 메소드를 호출할 때, Python은 자동으로 메소드의 첫 번째 인수로 해당 객체를 넘겨줍니다. 따라서 메소드를 호출할 때는 self를 따로 지정해 주지 않아도 됩니다.

__init__ 메소드

__init__ 메소드는 C++과 Java의 생성자와 유사합니다. 클래스의 객체가 인스턴스화될 때 즉시 실행됩니다. 이 메소드는 객체를 초기화할 때 필요한 설정을 수행하는데 유용합니다.

Person 클래스를 예시로 다시 들어보겠습니다.

class Preson:
	def __init__(self, name, ago):
    	self.name. = name
        self.ago = ago
       
    def greet(self):
    	print(f"My name is {self.name} and I'm {self.age} years old")
 
 
# 객체 생성 및 사용
person1 = Person("감자", 28)
person2 = Person("Roxie", 33)
person1.greet()
person2.greet()

이 코드는 Person 클래스의 인스턴스인 person1과 person2를 생성하고, 각 객체의 이름과 나이를 초기화한 후, introduce 메소드를 호출하여 각 사람을 소개합니다.


3. Polymorphism in Python (다형성)

Polymorphism : 많은 형태를 가짐

Python에서는 다형성을 통해 서로 다른 클래스의 객체들이 동일한 메소드를 갖지만, 그 메소드가 서로 다른 동작을 할 수 있도록 할 수 있습니다. 이는 메소드 오버라이딩(재정의)을 통해 이루어집니다. 즉, 하위 클래스가 상위 클래스의 메소드를 자신에 맞게 변경하여 사용하는 것입니다.

예를 들어, '새'라는 기본 클래스가 있고, '참새'와 '타조'라는 하위 클래스가 있습니다. 모든 새는 날 수 있다고 하지만, 타조는 날지 못합니다. 이를 코드로 표현하면 아래와 같습니다:

class Bird:
    def intro(self):
        print("There are many types of birds.")

    def flight(self):
        print("Most of the birds can fly but some cannot.")

class Sparrow(Bird):
    def flight(self):
        print("Sparrows can fly.")

class Ostrich(Bird):
    def flight(self):
        print("Ostriches cannot fly.")

이제 객체를 생성해서 사용하는 경우를 나눠서 보겠습니다.

1) 객체 생성

obj_bird = Bird()
obj_spr = Sparrow()
obj_ost = Ostrich()

2) 일반 새에 대해 설명하는 메서드

obj_bird.intro()
obj_bird.flight()

# 출력
# There are many types of birds.
# Most of the birds can fly but some cannot.

3) 앵무새에 대해 설명하는 메서드

obj_spr.intro()
obj_spr.flight()

# 출력
# There are many types of birds.
# Sparrows can fly.

4) 타조에 대해 설명하는 메서드

obj_ost.intro()
obj_ost.flight()

# 출력
# There are many types of birds.
# Ostriches cannot fly.

여기서 Bird 클래스는 모든 새가 날 수 있다고 말하지만, Ostrich 클래스는 날지 못한다고 오버라이딩하여 다른 동작을 합니다.


4. Encapsulation in Python (캡슐화)

캡슐화는 객체 지향 프로그래밍에서 Objects와 Method를 하나로 묶는 것을 말합니다. 이를 통해 중요한 정보를 외부로부터 보호하고 클래스의 내부 로직을 숨길 수 있습니다.

조금 더 자세히 설명을 해보겠습니다.

1) 데이터 보호
클래스 내부에 중요한 데이터가 있을 시 캡슐화를 사용하면, 이 데이터에 대한 접근을 제한하여 잘못된 방법으로 사용되거나 변경되는 것을 방지할 수 있습니다.

계좌의 잔액 정보와 같이 중요한 정보를 외부에서 직접 수정하지 못하도록 할 때 사용되기도 합니다.

2) 비공개 속성
Python에서는 변수명 앞에 언더스코어 두 개(__)를 붙여 비공새 속성을 만들 수 있습니다. 이렇게 선언된 속성은 외부나 자식 클래스 직접 접근할 수 없어 데이터를 더 안전하게 보호할 수 있습니다.

class Base:
    def __init__(self):
        self.a = "공개 속성"
        self.__c = "비공개 속성"  # 이 속성은 클래스 외부에서 접근 불가

class Derived(Base):
    def __init__(self):
        Base.__init__(self)
        # 다음 코드는 비공개 속성에 접근하려 하므로 오류를 발생시킵니다:
        # print(self.__c)

# 드라이버 코드
obj1 = Base()
print(obj1.a)  # 공개 속성 접근 가능

# 비공개 속성 접근 시도 (오류 발생)
# print(obj1.__c)  # 이 줄을 실행하면 오류가 발생합니다.



5. Inheritance in Python (상속)

Inheritance는 한 클래스가 다른 클래스의 속성을 물려받을 수 있는 기능을 말합니다.

부모 클래스 : 속성을 물려주는 클래스
자식 클래스 : 속성을 물려받는 클래스

장점

Inheritance의 장점은 아래와 같습니다.

1) 실제 관계를 잘 표현할 수 있습니다.
2) 코드의 재사용성을 높여줍니다.
3) 전이적입니다. 즉, 클래스 B가 클래스 A로부터 상속받는다면, B의 모든 하위 클래스도 자동으로 A로부터 상속받습니다.

유형

상속의 유형은 네가지로 나뉘는데요, 이를 자세히 알아보도록 하겠습니다.

1) 단일 상속 : 하나의 부모 클래스 - 하나의 자식 클래스
2) 다중 상속 : 여러 부모 클래스 - 하나의 자식 클래스
3) 계층적 상속 : 하나의 부모 클래스 - 여러개의 자식 클래스
4) 다단계 상속 : 자식 클래스가 부모 클래스로부터 속성을 상속받고, 그 부모 클래스도 다른 부모 클래스로부터 속성을 상속

예시

예시에서 Person 클래스는 부모 클래스로, Employee 클래스는 이를 상속받는 자식 클래스입니다.

코드를 한 번에 보면 헷갈리니 나눠서 보도록 하겠습니다.

1) 부모 클래스와 자식 클래스

# 부모 클래스
class Person:
    def __init__(self, name, idnumber):
        self.name = name
        self.idnumber = idnumber

    def display(self):
        print(self.name)
        print(self.idnumber)
        
    def details(self):
        print("내 이름은 {}".format(self.name))
        print("ID 번호: {}".format(self.idnumber))
    
# 자식 클래스
class Employee(Person):
    def __init__(self, name, idnumber, salary, post):
        self.salary = salary
        self.post = post

        # 부모 클래스의 생성자 호출
        Person.__init__(self, name, idnumber)
        
    def details(self):
        print("내 이름은 {}".format(self.name))
        print("ID 번호: {}".format(self.idnumber))
        print("직책: {}".format(self.post))

2) 객체 생성

a = Employee('감자', 9703289, 200000, "매니저")

3) 부모 클래스의 display 메소드 호출

a.display()

출력물은 다음과 같습니다
감자
9703289

4) 부모 클래스의 details 메소드 호출

a.details()

출력물은 다음과 같습니다.
내 이름은 감자
ID 번호: 9703289
직책: 매니저

참고로 예시의 클래스는 단일 상속입니다.



6. Data Abstraction in Python (데이터 추상화)

데이터 추상화는 사용자로부터 불필요한 코드의 세부사항을 숨기는 프로그래밍 기법입니다. 이를 통해 사용자는 복잡한 내부 구현을 알 필요 없이 간단한 인터페이스를 사용하여 기능을 활용할 수 있습니다. 또한, 데이터 추상화는 코드의 민감한 부분이 외부에 노출되는 것을 방지하므로, 보안성을 높이는 데에도 도움을 줍니다.

중요성

1) 간소화된 인터페이스
복잡한 내부 구현을 걱정하지 않고, 제공된 메소드를 통해 원하는 작업을 수행할 수 있습니다.

2) 보안 향상
중요한 데이터나 로직을 외부에 공개하지 않음으로써, 안전하게 정보를 보호할 수 있습니다.

3) 유지보수 용이
내부 구현을 변경하더라도 외부 인터페이스에 영향을 주지 않기 때문에, 시스템의 유지보수가 용이해집니다.

예시

Abstraction는 보통 중요한 로직을 숨기고 사용자에게는 필요한 기능만 제공할 때 사용합니다.

예시를 보겠습니다.

이번에도 코드를 나눠서 보도록 하겠습니다.

1) 클래스 생성
아래의 예시의 클래스는 계좌의 잔액을 증가시키거나 감소시키는 기능이 있습니다. 사용자는 이러한 메소드를 호출하여 잔액을 관리할 수 있지만, 계좌의 잔액을 계산하거나 변경하는 내부 로직은 볼 수 없습니다.

class BankAccount:
    def __init__(self, owner, initial_balance=0):
        self.owner = owner
        self.__balance = initial_balance  # 비공개 속성으로 잔액을 숨김

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount  # 입금액을 잔액에 추가
            print(f"{amount}원이 입금되었습니다.")
        else:
            print("입금 금액은 양수여야 합니다.")

    def withdraw(self, amount):
        if amount > 0 and amount <= self.__balance:
            self.__balance -= amount  # 출금액을 잔액에서 차감
            print(f"{amount}원이 출금되었습니다.")
        else:
            print("출금 금액은 잔액을 초과할 수 없습니다.")

    def display_balance(self):
        print(f"계좌 잔액은 {self.__balance}원입니다.")

2) 객체 생성

account = BankAccount("감자", 100000)

3) 메소드 호출

account.deposit(50000)
account.withdraw(20000)
account.display_balance()

출력물은 아래와 같습니다.
50000원이 입금되었습니다.
20000원이 출금되었습니다.
계좌 잔액은 130000원입니다.



지금까지 OOP에 대해 알아보았는데요,
공부를 하면서 실무에 OOP를 많이 적용해보면서 적응을 해봐야겠다는 생각이 들었습니다.

오늘도 제 블로그가 여러분에게 도움이 되었으면 좋겠다는 생각을 하며, 오늘 포스팅 여기서 마치도록 하겠습니다.

그럼 안뇽!

profile
감자와 함께 떠나는 프로그래밍 여행

0개의 댓글