1.객체지향(Object Oriented)의 이해
2. 클래스(Class) 정의
3. 클래스(Class)와 인스턴스(Instance)의 특징
4. 클래스 상속(Inheritance)
5. Q&A
6. 마치며
우리는 지금까지 파이썬이 제공하는 클래스의 기능을 이용하여 손쉽게 코딩을 할 수 있었습니다.
이건 바로, '코드 재사용'의 효과이죠 😊
그렇다면, 우리가 만든 코드를 재사용할 수 있다면 어떨까요?
불필요한 작업이 축소 되고, 효율성이 향상되는 효과로,
개발의 생산성을 향상할 수 있겠죠.
객체지향 프로그래밍은 이런 것들을 가능하게 해줌으로써,
대규모 프로그램을 효율적으로 코딩할 수 있게 해줍니다.
- 객체(Object)
: 변수(Variable)과 메서드(Method)를 서로 연관된 것들끼리 묶은 것- 변수(Variable) : 값을 가집니다.
- 메서드(MEthod) : 실행 코드를 가집니다.
예를 들어, 자동차를 볼까요?
변수로는 연료량
, 속도계
등이 있겠죠.
Method로는 주행기능
등이 있겠죠.
그런데 주행기능
은 변수인 연료량
, 속도계
등에 영향을 주는데요,
Method는 변수와 연관된 기능인거죠.
이처럼, 서로 연관된 변수(Variable)와 메서드(Method)를 잘 파악해 묶어 객체를 형성하는 것이 중요합니다.
따라서, 객체(Object)는 하나의 레고블럭처럼 생각할 수 있는데요,
이는 객체지향 프로그래밍의 매우 중요한 개념이며,
다음과 같은 매우 중요한 특징 두 가지를 가집니다.
부품화 : 객체를 하나의 부품처럼 사용할 수 있습니다.
재사용성 : 객체는 여러 번 재사용할 수 있습니다.
위에서 살펴본 객체는 '클래스(Class)'를 통해서 만들 수 있는데요, 자세히 살펴봅시다.
- 클래스(Class)
: 부품 객체를 만들기 위한 템플릿 / 설계도 / 청사진
: 추상화(Abstraction)의 과정을 통해 만들어집니다.
위에서 예시로 본 자동차를 클래스로 본다면,
자동차 객체가 가져야할 변수는 연료량
, 속도
가 되고, Method로는 주행기능
이 있겠네요.
- 객체지향의 구성요소
: 클래스(Class) + 객체(Object) + 메서드(Method)
클래스(Class)
: 같은 영역에 속하는 속성(attribute)과 행위(behavior)를 정의한 것입니다.
: 객체지향 프로그램의 기본적인 사용자 정의 데이터 타입입니다.
객체(Object) = 인스턴스(Instance : 실체화된 객체 == 실제로 사용된 객체)
ex) 객체 : 자동차, 트럭은 자동차의 인스턴스
: 메모리에 로딩된 클래스를 통해, 클래스를 템플릿으로 하여 메모리 상에 생성된 정보입니다.
: 자신 고유의 속성을 가지며 클래스에서 정의한 행위를 수행합니다.
: 객체의 행위는 클래스에서 정의된 행위에 대한 정의를 공유함으로써, 메모리를 효율적으로 사용합니다.
메서드(Method) = 메시지(Message)
: 클래스로부터 생성된 객체 사용 시, 객체에 명령을 내리는 행위입니다.
('객체가 가지고 있는 메서드를 호출한다')
(= '객체에 메시지를 전달한다')
: 한 객체의 속성을 조작할 목적으로 사용합니다.
: 객체 간의 통신은 메시지 전달을 통해 이루어집니다.
- 객체지향 프로그래밍(Object Oriented Programming)
: 상태와 행위로 이루어진 객체를 만든 다음, 해당 객체들을 레고 블럭처럼 조립하여, 하나의 프로그램을 만드는 것
즉, 객체를 만들고, 객체를 이용해 문제를 해결하려는 프로그래밍 방법입니다.
- 추상화(Abstraction)
: 객체에서 공통된 속성과 행위를 추출하여 type을 정의하는 과정입니다.
예를 들어, 학생 A
, B
, C
가 있다고 해봅시다.
이들은 학생
이라는 Type으로 정의할 수 있습니다.
공통된 속성으로는, 학번
, 이름
, 주민번호
, 학과
등이 있겠죠.
공통된 행위로는, 수강 신청
, 수강 취소
등이 있겠죠.
쉽게 말하면,
불필요한 정보는 숨기고 중요한 정보만 표현해 프로그램을 간단히 만드는 것입니다.
- '추상 데이터 타입'
: 추상화 과정을 통해 만들어진 Type입니다.
추상 데이터 타입은
데이터 타입의 표현과 연산은 캡슐화
접근 제어를 통해 데이터의 정보를 은닉
할 수 있습니다.
즉,
입니다.
- 상속 (Inheritance)
: 새로운 클래스가 기존의 클래스의 데이터와 연산을 이용할 수 있게 하는 기능입니다.
: 상위 클래스에서 상속하거나, 하위 클래스에서 상속받음으로써, 속성과 행위를 공유합니다.
상속 관계의 클래스는 다음과 같이 부르기도 합니다.
(상속하는 클래스) - (상속받는 클래스)
: (상위 - 하위), (부모 - 자식), (슈퍼 - 서브), (기반 - 파생)
상속(Inheritance)을 통해
하위 클래스에서 프로그램의 요구에 맞추어 클래스를 수정
클래스 간의 종속 관계를 형성하여 객체를 조직화
할 수 있습니다.
상속의 효과로는
재사용으로 인해 코드가 줄어듭니다.
(속성이나 행위를 다시 정의할 필요가 없기 때문이죠)
범용적인 사용을 가능하게 합니다.
(예를 들어, object 타입의 매개변수에는 string / int 객체가 쓰여도 괜찮습니다.)
(string/ int 모두 object를 상속받기 때문이죠)
자료와 Method의 자유로운 사용 및 추가를 할 수 있습니다.
- 다형성 (Polymorphism)
: 다양한 형태로 나타날 수 있는 특징입니다.
: 어떤 한 요소에 여러 개념을 넣어 놓는 것입니다.
객체지향 프로그래밍은,
하나의 클래스에서 같은 이름의 행위를 여러번 정의하거나,
상위 클래스의 행위를 하위 클래스에서 재정의 할 수 있기 때문에
다형성을 가집니다.
- 오버라이딩(Overriding)
: 같은 이름의 Method가 여러 클래스에서 다른 기능을 하는 것입니다.
- 오버로딩(Overloading)
: 같은 이름의 Method가 인자의 개수나 자료형에 따라, 다른 기능을 하는 것입니다.
- 매서드 오버라이딩(Method Overriding)
: 상속으로 물려받은 자료나 Method를 그대로 사용하지 않고, 하위 클래스에서 새로 정의해 사용하는 방법입니다.
즉, 동일한 이름의 Method가 다른 클래스에서 다르게 작동되는 것 입니다.
단, 상위 클래스의 Method와 동일한 서명(Signature : 매개변수의 타입, 개수, 반환 타입)을 가져야 합니다.
- 매서드 오버로딩(Method Overloading)
: 클래스 내부에, 동일한 이름의 행위를 여러 개 정의하는 것
즉, 동일한 이름의 Method가 같은 클래스에서 다르게 작동되는 것입니다.
Method 이름을 하나로 통일 가능하며,
같은 이름의 Method에 여러 종류의 매개 변수를 받을 수 있습니다.
따라서,
Method의 1) 이름은 같아야 하며, 2) 매개변수의 타입과 수는 서로 달라야 합니다.
단, 반환 타입은 상관없습니다.
- 클래스 정의
class (클래스명): ...
클래스의 코드 블록 안에 field와 Method를 정의해 사용할 수 있습니다.
- 객체 생성
(변수) = (클래스명)() # 생성자 Method
생성자 Method : 클래스 이름과 동일한 Method입니다.
다음은 Person
이라는 클래스를 정의하고,
member
라는 instance(객체)를 생성하는 코드입니다.
class Person: # 클래스 정의
pass
member = Person() # 객체 생성 : 생성자 Method
if isinstance(member, Person):
print(f"member는 Person의 instance입니다.")
[Result]
member는 Person의 instance입니다.
- 생성자 메서드(Constructor Method)
: 객체를 생성하기 위해 호출되는 함수입니다.
: 클래스의 이름과 동일합니다.
: 실행 시__init__
Method가 실행됩니다.class (클래스명): def __init__(self, 매개변수 목록): ...
- 소멸자 메서드(Deconstructor Method)
: 객체를 소멸시키기 위해 호출되는 함수입니다.
: 객체가 소멸되기 전에 호출되고, 매번 정의할 필요는 없습니다.
: 실행 시__del__
Method가 실행됩니다.class (클래스명): ... def __del__(self): ...
소멸자 Method는 생성자와 다르게,
self
를 제외한 매개변수는 사용하지 않습니다.
self
- self
: 클래스에 의해 생성된 객체 공간을 가르키는 식별자
: 객체 공간의 field와 Method에 접근할 경우,self.(식별자)
형식으로 사용합니다.
: Keyword는 아니지만, 관습적으로self
라는 이름으로 사용합니다.
다음은 Person
이라는 클래스에서 생성자와 소멸자를 정의하고,
member
라는 객체를 생성하여 정보를 출력하는 코드입니다.
class Person:
def __init__(self, name, age): # 생성자 (Constructor)
self.name = name
self.age = age
print("객체가 생성되었습니다.")
def __del__(self): # 소멸자 (Deconstructor)
print("객체가 제거되었습니다.")
member = Person("Wonder_Land", 20) # 생성자 Method
print(member.name, member.age, sep = " ")
print(dir(member))
[Result]
객체가 생성되었습니다.
Wonder_Land 20
['__class__', '__del__', '__delattr__', ..., 'age', 'name']
객체가 제거되었습니다.
- 인스턴스 메서드(Instance Method)
:self
가 가르키는 객체의 field 정보에 접근해, 특정 기능을 수행하도록 정의된 Method입니다.
다음은 위에서 살펴본 코드에서,
to_str
이라는 인스턴스 Method를 정의하는 코드입니다.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
print("객체가 생성되었습니다.")
def __del__(self):
print("객체가 제거되었습니다.")
def to_str(self): # 인스턴스 메서드 (Instance Method)
return f"{self.name} {self.age}"
member = Person("Wonder_Land", 20) # 생성자 Method
print(member.to_str()) # 인스턴스 매서드 호출
[Result]
객체가 생성되었습니다.
Wonder_Land 20
객체가 제거되었습니다.
- 인스턴스 변수(Instance Variable)
: 클래스 내에서self.(변수)
형식를 가지는 변수입니다.
: 객체마다 가지고 있는 객체 고유의 정보입니다.
다음은 위에서 살펴본 코드에서,
Field의 접근 제한이 없는 경우입니다.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
print("객체가 생성되었습니다.")
def __del__(self):
print("객체가 제거되었습니다.")
def to_str(self): # 인스턴스 메서드 (Instance Method)
return f"{self.name} {self.age}"
외부접근의 제한을 막기 위해,
변수 name
, age
는 캡슐화된 필드로 만드는 것이 필요할 수 있습니다.
또한 위의 코드처럼,
외부에 노출된 field는 입력 시 유효성 검사를 할 수 없으므로 잘못된 값을 저장할 수 있습니다.
예를 들어, age
에 음수가 들어올 수도 있는거죠.
따라서, 입력 데이터의 검증을 위해,
적절한 멤버 field의 접근 제한이 필요합니다.
Python에서는
외부에서 field에 접근하는 것을 제한하는
인스턴스 변수의 접근 제한 기능을 지원하고 있습니다.
- 인스턴스 변수의 접근 제한 기능
인스턴스 변수 앞에__
를 붙여줌으로써, 프라이빗 필드(Private Field)를 생성할 수 있습니다.class (클래스명): ... self.__(변수명)
이렇게 프라이빗 필드를 만들어줄 때는,
getter / setter
Method의 제공 여부에 대한 고민이 필요합니다.
getter
와 setter
모두 제공getter
만 제공setter
만 제공getter
와 setter
- getter Method
: 멤버를 읽어오는 Method
- setter Method
: 멤버를 변경하는 Method
다음 코드에서는,
인스턴스 변수 name
, age
를 프라이빗 필드로 구성하고,
getter
와 setter
를 활용합니다.
class Person:
...
def get_name(self): # getter : __name 필드의 값을 반환
return self.__name
def get_age(self): # getter : __age 필드의 값을 반환
return self.__age
def set_age(self, age): # setter : __age 필드의 값을 변경
if age < 0:
raise TypeError("나이는 0 이상의 값만 허용")
self.__age = age
member = Person("Wonder_Land", 20) # 생성자 Method
member.set_age(-20)
print(member.to_str())
[Result]
TypeError: 나이는 0 이상의 값만 허용
get_name
Method는,
프라이빗 필드인 __name
를 반환하는 getter
Method이며,
__name
field에서는 getter
Method만 제공하는 것을 알려줍니다.
set_age
Method는,
프라이빗 필드인 __age
를 변경하는
setter
Method이며,
만약 음수가 들어오면, TypeError를 일으킵니다.
Python에서는,
getter와 setter를 대신할 수 있는 기능인
'데코레이터(Decorator)'를 지원합니다.
- 데코레이터(Decorator)
: 변수 이름과 같은 Method를 만들어 사용할 수 있습니다.
:@property
를 이용합니다.
- getter로 사용할 때
class (클래스): ... @property # getter : 데코레이터 (Decorator) def (변수)(self): ...
- setter로 사용할 때
class (클래스): ... @property의 이름.setter # setter : 데코레이터 (Decorator) def (변수)(self): ...
다음은 위의 코드를,
데코레이터 기능을 이용하여 만들어보았습니다.
name
Method는,
Method이지만 변수처럼 사용할 수 있으며,
__name
필드값을 반환하는 getter의 역할입니다.
age
Method는,
Method이지만 변수처럼 사용할 수 있으며,
__age
필드값을 반환하는 getter와
__age
필드값을 변경하는 setter 역할입니다.
class Person:
...
# 데코레이터 (Decorator) - getter
@property
def name(self):
return self.__name
# 데코레이터 (Decorator) - getter
@property
def age(self):
return self.__age
# 데코레이터 (Decorator) - setter
@age.setter
def age(self, age):
if age < 0:
raise TypeError("나이는 0 이상의 값만 허용")
self.__age = age
member = Person("Wonder_Land", 20) # 생성자 Method
member.age = 30 # 변수처럼 사용
print(member.to_str())
[Result]
객체가 생성되었습니다.
Wonder_Land 30
객체가 제거되었습니다.
- 클래스 변수(Class Variable)
: 클래스 내에서(클래스명).(변수)
형식를 가지는 변수입니다.
(인스턴스 변수(Instance Variable)는, 클래스 내에서self.(변수)
형식를 가지는 변수입니다.)
class (클래스명): (클래스변수) = (값) ...
(클래스명).(클래스변수)
다음은 클래스 변수를 활용한 예시입니다.
class Person:
count = 0 # 클래스 변수 (Class Variable)
def __init__(self, name, age):
self.__name = name
self.__age = age
Person.count += 1 # 클래스 변수의 접근
print("객체가 생성되었습니다.")
...
member = Person("Wonder_Land", 20)
print(member.to_str())
print(f'Person 클래스의 instance는 {Person.count}개 입니다.')
[Result]
객체가 생성되었습니다.
Wonder_Land 30
Person 클래스의 instance는 1개 입니다.
객체가 제거되었습니다.
- 클래스 메서드(Class Method)
: 클래스가 소유하는 Method입니다.
class (클래스명): ... @classmethod def (클래스메서드)(cls, (매개변수목록)): ...
데코레이터(Decorator)인 @classmethod
를 사용하며,
cls
를 통해 클래스 자신에 대한 참조를 전달합니다.
(클래스명).(클래스매서드)(매개변수목록)
다음은 클래스 메서드를 활용한 예시입니다.
class Person:
...
@classmethod # 클래스 메서드
def get_info(cls):
return f"Person 클래스의 instance는 총 {cls.count}개 입니다."
member = Person("Wonder_Land", 20)
print(Person.get_info())
[Result]
객체가 생성되었습니다.
Person 클래스의 instance는 1개 입니다.
객체가 제거되었습니다.
클래스 메서드인 get_info()
에서
cls
를 인자로 하여, 클래스 자신을 참조하게 됩니다.
따라서 cls.count
는 Person.count
와 동일하다고 볼 수 있습니다.
조금 헷갈릴수도 있는데요,
인스턴스 변수 / 메서드는 Instance 단위의 변수 / 메서드를 사용할 때,
클래스 변수 / 메서드는 Class 단위의 변수 / 메서드를 사용할 때
각각 사용한다고 생각하면 됩니다.
그리고 getter / setter, 데코레이터는 인스턴스 변수를 다루기 위해 사용한다고 생각하면 되겠죠?
Python에서 기본적으로 제공하는 클래스들은,
클래스 내에 정의된 각 Method은 기본적으로 연산자들이 mapping되어 있습니다.
하지만, 우리가 직접 만드는 사용자 정의 클래스에서는 그렇지 않기 때문에(ㅠㅠ),
연산자를 중복해서 정의해줘야 합니다.
- 연산자 오버로딩(Oerator Overloading)
: 기존에 사용하는 연산자를 재정의 하여 사용자 정의 클래스로 사용하는 것입니다.
다음은 비교연산자에 대해 오버로딩을 진행한 예시입니다.
class Person:
...
def __gt__(self, other): # Greater than
return self.age > other.age
def __ge__(self, other): # Greater than or Eqaul
return self.age >= other.age
def __lt__(self, other): # Less than
return self.age < other.age
def __le__(self, other): # Less than or Eqaul
return self.age <= other.age
def __eq__(self, other): # Eqaul
return self.age == other.age
def __ne__(self, other): # Not Eqaul
return self.age != other.age
member1 = Person("A", 20)
member2 = Person("B", 30)
# 비교 연산자 (__le__) 사용
print(member1 <= member2)
[Resutl]
True
비교연산자 말고도,
+
, -
, *
와 같은 연산자도 오버로딩할 수 있습니다.
__str()__
메서드
- str() Method
:__str()__
Method를 통해,str()
함수에 객체를 전달하여 문자열로 변환할 수 있습니다.
class Person:
...
# __str()__ 매서드를 통해 문자열 반환
def __str__(self):
return f"{self.__name} {self.__age}"
member = Person("A", 20)
print(str(member)
[Resutl]
A 20
- 클래스 상속(Inheritance)
: 부모 클래스의 동작을 자식 클래스에서 재사용, 확장, 수정하는 것을 말합니다.class (클래스명)(부모클래스)
- 부모 클래스(Base / Parenet Class)
: 멤버가 상속되는 클래스- 자식 클래스(Derived / Child Class)
: 멤버를 상속하는 클래스
다음은 부모 클래스인 Parent
와
자식 클래스인 Child
를 작성하는 코드입니다.
class Parent:
def __init__(self, family_name):
self.__family_name = family_name
print("Parent 클래스의 __init__ 실행")
@property
def family_name(self):
return self.__family_name
class Child(Parent): # Parent 클래스를 상속
def __init__(self, first, last):
Parent.__init__(self, last)
# super().__init__(last) # super() 이용
self.__first = first
print("Child 클래스의 __init__ 실행")
child = Child("Wonder", "Land")
[Result]
Parent 클래스의 __init__ 실행
Child 클래스의 __init__ 실행
이 때, Parent.__init__(self, last)
클래스 이름을 사용하지 않고,
부모 클래스를 반환하는 super() 호출과 생성자 호출을 결합해 동일한 효과를 얻을 수 있습니다.
- 매서드 오버라이딩(Method Overriding)
: 부모 클래스의 Method와 동일한 서명(Signature)를 가진 Method를, 자식 클래스에서 다시 정의해 사용하는 것입니다.
class Parent:
...
def print_info(self):
print(f'Parent : {self.family_name}')
class Child(Parent): # Parent 클래스를 상속
...
def print_info(self): # 매서드 오버라이딩
Parent.print_info(self)
# super.print_info()
print(f'Child: {self.name}')
child = Child("Land", "Wonder")
child.print_info()
[Result]
Parent 클래스의 __init__ 실행
Child 클래스의 __init__ 실행
Parent : Wonder
Child: Wonder Land
이 때, child.print_info()
는
Parent Class의 print_info()
가 아닌,
Child Class의 print_inof()
를 실행합니다.
또한 이 때도 마찬가지로,
부모 클래스를 반환하는 super() 호출을 이용해 동일한 효과를 얻을 수 있습니다.
-
오늘은 'Programming Beginner' - '파이썬 프로그래밍 기초(2) 파이썬의 기본 응용'의 마지막 수업인,
객체지향을 배웠습니다.
이론적인 부분이 많아서 헷갈릴 수 있지만,
실제로 사용해보면 금방 알 수 있는 개념입니다.
이 객체지향을 배우기 전과 후에 프로그래밍을 한 결과를 보면 상당히 다를 수 있는데요,
객체를 이용해 조직적으로 짜 본 프로그램은
훨씬 보기도 좋고 이해하기도 훨씬 수월하죠
객체 단위로 프로그래밍을 하다보면,
객체 단위로 생각할 수 있고,
이는 독자로 하여금 이해하기 쉽게하기 때문이죠.
객체지향의 특징 중 하나인 상속도 마찬가지죠.
프로그램이 훨씬 구체적이고 조직적입니다.
이제 Python의 기본적인 이론에 대해 공부해봤는데요,
백준 문제들을 풀면서 Python의 감을 잃지 않고 더 익숙해져야 겠네요!
아마 다음 공부는 'C언어'가 되지 않을까 싶네요.
C -> C++ -> JAVA 순으로 공부하고,
언어들의 기본적인 사용법을 배우고 난 후,
(사실 이미 배워봤고, Python도 다시 복귀해봤으니 아마 더 빨리 복귀할 수 있을 것 같아요! 😊)
자료구조 / 알고리즘을 Python, C++로 배워볼까 합니다.
그리고 그 다음은 정말 분야(전공)를 하나 정해서 공부해야겠어요.
(복학하기 전까지 할 수 있겠죠...?? 🤣)
그 때까지 열심히 해야겠네요...
안 놀게요...
할게 많아요 😂
[Reference] : 위 글은 다음 내용을 참고, 인용하여 만들어졌습니다.
- 전반적 내용 : 삼성 SW Expert Academy