07. 객체지향 프로그래밍¶
객체지향 프로그래밍
객체(Object)
객체지향프로그래밍(Object Oriented Programming)
클래스(Class)와 인스턴스(Instance)
변수, 이름공간, 메서드
상속
7.1 객체¶
Python에서 모든 것은 객체(object) 입니다.
모든 객체는 타입(type), 속성(attribute), 조작법(method) 을 가집니다.
객체(Object)의 특징
타입(type): 어떤 연산자(operator)와 조작(method)이 가능한가?
속성(attribute): 어떤 상태(데이터)를 가지는가?
조작법(method): 어떤 행위(함수)를 할 수 있는가?
7.1.1 타입과 인스턴스¶
type |
instance |
---|---|
|
|
|
|
|
|
|
|
타입 (Type)¶
공통된 속성 (attribute)과 조작법 (method)을 가진 객체들의 분류
인스턴스 (Instance)¶
특정 타입(type)의 실제 데이터 예시(instance)입니다.
파이썬에서 모든 것은 객체이고, 모든 객체는 특정 타입의 인스턴스입니다.
a = 10
b = 20
# a, b는 10, 20을 가리키는 이름 -- namespace
# 10, 20 (a, b가 가리키는 대상)은 객체 -- object
# 10, 20 은 int 타입(type)의 인스턴스
# a 의 type이 int형인지 is 연산자로 확인해 봅시다.
a = 10
type(a) is int
True
int()
int()
는 int
Instance를 만드는 함수입니다.
# isinstance 함수를 활용해 확인해 봅시다.
isinstance(a, int)
True
7.1.2 속성과 메서드¶
객체의 속성(상태, 데이터)과 조작법(함수)을 명확히 구분해 봅시다.
type |
attributes |
methods |
---|---|---|
|
|
|
|
_ |
|
|
_ |
|
|
_ |
|
속성 (Attribute)¶
속성(attribute)은 객체(object)의 상태/데이터를 뜻합니다.
활용법
<객체>.<속성>
예시
3+4j.real
complex
타입 인스턴스가 가진 속성을 확인해봅시다.
# 복소수를 만들어보고, 타입을 출력해봅시다.
img_number = 3 + 4j
print(type(img_number))
<class 'complex'>
# 허수부랑 실수부 .real과 .imag 속성을 통해 각각 출력해봅시다.
# complex 객체의 실수 속성과 허수 속성이라고도 표현 가능합니다.
print(img_number.real)
print(img_number.imag)
3.0
4.0
메서드 (Method)¶
특정 객체에 적용할 수 있는 행위(behavior)를 뜻 합니다.
활용법
<객체>.<메서드>()
예시
[3, 2, 1].sort()
list
type의 인스턴스에 적용 가능한 조작법(method)을 확인해 봅시다.
# 리스트를 하나 만들고 정렬해봅시다.
# 리스트 타입 객체의 sort() 메서드로 정렬 가능합니다.
numbers = [3, 2, 1]
numbers.sort()
numbers
[1, 2, 3]
# list 타입의 객체들이 할 수 있는 것들을 알아봅시다. (list 타입 객체가 가지고 있는 모든 속성과 메서드를 보여줍니다.)
print(dir(list()))
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
# 1 + 1
(1).__add__(1)
2
7.2 객체지향 프로그래밍¶
객체지향 프로그래밍 (Object-Oriented Programming) : Object가 중심(oriented)이 되는 프로그래밍
wikipedia - 객체지향 프로그래밍
객체 지향 프로그래밍(영어: Object-Oriented Programming, OOP)은 컴퓨터 프로그래밍의 패러다임의 하나입니다.
객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 “객체”들의 모임으로 파악하고자 하는 것입니다.
7.2.1 절차 중심 vs. Object 중심¶
프로그래밍 패러다임: 어떻게 프로그램을 작성할 것인가
7.2.2 Object 중심의 장점¶
wikipedia - 객체지향 프로그래밍
객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용됩니다.
또한 프로그래밍을 더 배우기 쉽게 하고 소프트웨어 개발과 보수를 간편하게 하며, 보다 직관적인 코드 분석을 가능하게 하는 장점을 갖고 있습니다.
코드의 직관성
활용의 용이성
변경의 유연성
7.3 클래스와 인스턴스¶
클래스 (
class
) : 객체들의 분류(class)를 정의할 때 쓰이는 키워드
인스턴스 (
instance
) : 객체의 실체와 예, 인스턴스(instance)를 활용
7.3.1 클래스 (Class) 생성¶
클래스 생성은
class
키워드와 정의하고자 하는<클래스의 이름>
으로 가능합니다.<클래스의 이름>
은PascalCase
로 정의합니다.클래스 내부에는 데이터와 함수를 정의할 수 있고, 이때 데이터는 속성(attribute) 정의된 함수는 **메서드(method)**로 부릅니다.
활용법
class <클래스이름>:
<statement>
class ClassName:
statement
# Person 이라는 이름의 class를 정의해봅시다.
# 아래와 같이 """(docstring)을 통해 설명(doc)도 함께 정의해 봅시다.
class ClassName:
"""
This is DOCSTRING
"""
class Person:
"""
이것은 Person 클래스(class) 입니다.
"""
print(Person.__doc__)
이것은 Person 클래스(class) 입니다.
print(dir(Person))
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
# 정의한 Person 클래스의 type을 출력해 봅시다
type(Person)
type
7.3.2 인스턴스 (Instance) 생성¶
정의된 클래스(
class
)에 속하는 객체를 해당 클래스의 인스턴스(instance)라고 합니다.Person
클래스의 인스턴스는Person()
을 호출함으로써 생성됩니다.type()
함수를 통해 생성된 객체의 클래스를 확인할 수 있습니다.
활용법
# 인스턴스 = 클래스()
person1 = Person()
person1
은 사용자가 정의한(user-defined)Person
이라는 데이터 타입(data type)의 인스턴스입니다.
# Person 클래스의 인스턴스 p1과 p2를 생성해봅시다.
# 두 인스턴스의 type을 출력하고
# 우리가 위에서 정의한 doc를 p1.__doc__, p2.__doc__ 속성을 통해 출력해봅시다.
p1 = Person()
p2 = Person()
print(type(p1), type(p2))
print(p1.__doc__, p2.__doc__)
<class '__main__.Person'> <class '__main__.Person'>
이것은 Person 클래스(class) 입니다.
이것은 Person 클래스(class) 입니다.
7.3.3 메서드 (Method) 정의¶
특정 데이터 타입(또는 클래스)의 객체에 공통적으로 적용 가능한 행위(behavior)들을 의미합니다.
활용법
class Person:
# 메서드(method)
def talk(self): # 인자로 self를 정의해봅시다.
print('안녕')
# 위와 똑같이 Person 클래스와 talk() 메서드를 정의해봅시다.
class Person:
# 메서드(method)
def talk(self): # 인자로 self를 정의해봅시다.
print('안녕')
# Person 클래스의 인스턴스 p1, p2를 생성하고 각각 talk 메서드를 실행해봅시다
p1, p2 = Person(), Person()
p1.talk()
p2.talk()
안녕
안녕
# 메서드도 함수이기 때문에 추가적인 인자를 받을 수 있습니다.
# Person 클래스를 재정의 하며 talk() 메서드를 정의하고, eat() 메서드를 추가로 정의해봅시다.
# eat() 메서드는 food 라는 이름의 인자를 받아서 출력합니다.
# 추가적인 인자를 받기 위해서는 아래와 같은 모습으로 코드가 작성되어야 합니다.
class MyClass:
def method1(self):
print('Hi')
def method2(self, arg):
print(arg)
# 기본 인자, 가변 인자 리스트 등 함수의 인자와 동일하게 매개변수를 정의할 수 있습니다.
# Person 클래스를 재정의하며, eat() 메서드를 정의할 때 food 인자의 기본 값을 원하는 음식으로 설정해봅시다.
class Person:
# 메서드(method)
def talk(self): # 인자로 self를 정의해봅시다.
print('안녕')
def eat(self, food = '치킨 먹고싶다..'):
print (f'{food} 냠냠')
# Person 클래스의 인스턴스 p1을 생성하고 eat() 메서드를 인자 있이/없이 두번 호출해봅시다.
p1 = Person()
p1.eat()
p1.eat('치킨')
치킨 먹고싶다.. 냠냠
치킨 냠냠
self
¶
인스턴스 자신(self)
Python에서 인스턴스 메서드는 호출 시 첫번째 인자로 인스턴스 자신이 전달되게 설계되었습니다.
보통 매개변수명으로
self
를 첫번째 인자로 정의 (다른 이름도 가능하지만 추천하지는 않습니다.)
# 아래의 Person 클래스를 통해서 self가 무엇인지 확인해봅시다.
class Person:
# self => 인스턴스 메서드의 1번 인자
def test(self):
print(self)
# Person 클래스의 인스턴스 p1을 생성하고 test() 메서드를 호출하여 self를 확인해봅시다.
p1 = Person()
p1.test()
p1
<__main__.Person object at 0x000001E400E77670>
<__main__.Person at 0x1e400e77670>
# p1을 출력하여 비교해봅시다.
p2 = Person()
p2.test()
p2
<__main__.Person object at 0x000001E400E774C0>
<__main__.Person at 0x1e400e774c0>
생성자 (constructor) 메서드¶
인스턴스 객체가 생성될 때 호출되는 함수. 반드시 __init__
이라는 이름으로 정의.
활용법
class MyClass:
def __init__(self):
print('생성될 때 자동으로 호출되는 메서드입니다.')
생성자를 활용하면 인스턴스가 생성될 때 인스턴스의 속성을 정의할 수 있습니다.
class Person:
def __init__(self, name):
self.name = name
def talk(self):
print(f'I am {self.name}')
# 자신의 이름을 인자로 넘기며 Person의 인스턴스 p1를 생성해봅시다.
p1 = Person('john')
# talk() 메서드를 호출해 봅시다.
p1.talk()
I am john
# talk 메서드를 클래스에서 메서드를 실행해봅시다. self는 정말 빈자리일까요?
Person.talk(p1) # 확인용 코드입니다. 이렇게 작성하지 않습니다.
I am john
소멸자 (destructor) 메서드¶
인스턴스 객체가 소멸(파괴)되기 직전에 호출되는 함수. 반드시
__del__
이라는 이름으로 정의.
활용법
def __del__(self):
print('소멸될 때 자동으로 호출되는 메서드입니다.')
# 생성자와 소멸자를 만들어봅시다.
# 생성자 메서드는 __init__으로, 소멸자 메서드는 __del__라는 이름으로 정의합니다.
class Person:
def __init__(self):
print('응애!')
def __del__(self):
print('떠날게..')
p3 = Person()
del p3 # 변수 삭제
응애!
떠날게..
p4 = Person()
p4 = list() # 변수 재할당
응애!
떠날게..
p5 = Person()
p6 = p5 # 인스턴스가 어딘가에 남겨져 있을 경우 호출되지 않습니다.
del p5
응애!
p6 = 1
떠날게..
7.3.4 속성 (Attribute) 정의¶
특정 데이터 타입(또는 클래스)의 객체들이 가지게 될 상태/데이터를 의미합니다.
self.<속성명> = <값>
혹은 <인스턴스>.<속성명> = <값>
으로 설정합니다
활용법
class Person:
def __init__(self, name):
self.name = name
def talk(self):
print(f'안녕, 나는 {self.name}')
# 인스턴스의 속성, 즉 개별 인스턴스들이 사용할 데이터를 정의해봅시다.
class Person:
def __init__(self, name):
self.name = name # 일반적으로 초기화할 때 설정합니다.
def talk(self):
print(f'안녕, 나는 {self.name}')
# 속성 정의는 꼭 생성자(__init__) 메서드에 작성해야 하지는 않지만, 일반적으로 생성자 메서드에 많이 작성합니다.
# 생성자 메서드를 통해 생성과 동시에 인스턴스 속성에 값을 할당할 수 있기 때문입니다.
# 새로운 Person의 인스턴스 p1을 이름과 함께 초기화 하고, 이름을 출력해봅시다.
p1 = Person('아이유')
p1.talk()
안녕, 나는 아이유
# 인스턴스 속성의 값을 변경할 수도 있습니다.
# 위에서 생성한 p1 인스턴스의 name을 다른 값으로 할당해보고, talk 메서드를 실행해봅시다.
p1.name = '이지은'
p1.talk()
안녕, 나는 이지은
# 생성자 메서드도 함수이기 때문에, 인자의 개수가 맞지 않으면 에러가 발생합니다.
# name인자 없이 새로운 Person의 인스턴스 p1을 생성해봅시다.
p1 = Person()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9912/1046008949.py in <module>
2 # name인자 없이 새로운 Person의 인스턴스 p1을 생성해봅시다.
3
----> 4 p1 = Person()
TypeError: __init__() missing 1 required positional argument: 'name'
# 위에서 생성한 인스턴스 p1을 del 연산자를 사용해 소멸시켜봅시다.
del p1
7.3.5 매직 (스페셜) 메서드¶
더블언더스코어(
__
)가 있는 메서드는 특별한 일을 하기 위해 만들어진 메서드이기 때문에스페셜 메서드
혹은매직 메서드
라고 불립니다.매직(스페셜) 메서드 형태:
__someting__
'__str__(self)',
'__len__(self)',
'__repr__(self)',
'__lt__(self, other)',
'__le__(self, other)',
'__eq__(self, other)',
'__ne__(self, other)',
'__gt__(self, other)',
'__ge__(self, other)',
__str__(self)
¶
class Person:
def __str__(self):
return '객체 출력(print)시 보여줄 내용'
특정 객체를 출력(
print()
) 할 때 보여줄 내용을 정의할 수 있습니다.
# dir() 함수를 통해 문자열 인스턴스가 활용 가능한 메서드를 확인해봅시다.
dir('')
['__add__',
'__class__',
'__contains__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getnewargs__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__iter__',
'__le__',
'__len__',
'__lt__',
'__mod__',
'__mul__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__rmod__',
'__rmul__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'capitalize',
'casefold',
'center',
'count',
'encode',
'endswith',
'expandtabs',
'find',
'format',
'format_map',
'index',
'isalnum',
'isalpha',
'isascii',
'isdecimal',
'isdigit',
'isidentifier',
'islower',
'isnumeric',
'isprintable',
'isspace',
'istitle',
'isupper',
'join',
'ljust',
'lower',
'lstrip',
'maketrans',
'partition',
'removeprefix',
'removesuffix',
'replace',
'rfind',
'rindex',
'rjust',
'rpartition',
'rsplit',
'rstrip',
'split',
'splitlines',
'startswith',
'strip',
'swapcase',
'title',
'translate',
'upper',
'zfill']
# Person 클래스를 정의해봅시다.
class Person:
def __init__(self, name):
self.name = name
# Person의 인스턴스 p1을 생성 후 출력해봅시다.
p1 = Person('john')
print(p1)
<__main__.Person object at 0x000001E400E77DC0>
# __str__() 매직메서드는 아래와 같이 정의합니다.
class Person:
def __init__(self, name):
self.name = name
def __str__(self): # __str__은 기억해두세요.
return f'나는 {self.name}'
def __repr__(self):
return f'I\'m {self.name}'
# 새로운 인스턴스 p2를 생성후 p2를 출력해봅시다.
p2 = Person('jane')
print(p2)
p2
나는 jane
I'm jane
실습¶
매직메서드를 활용하여 인스턴스간의 비교연산(
>
,==
)이 가능하도록 매직메서드를 정의해봅시다.
Person
클래스를 정의합니다. 인스턴스 속성은name
과age
를 가지며, 인스턴스간age
비교가 가능해야합니다.
__gt__
메서드와__eq__
메서드를 활용합니다.
# 실습
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'나는 {self.name}'
def __gt__(self, other):
return self.age > other.age
def __eq__(self, other):
return self.age == other.age
# 아래 코드가 정상 동작하도록 해봅시다.
p1 = Person('1', 1)
p2 = Person('2', 2)
p3 = Person('3', 1)
print(p1 > p2)
print(p1 == p3)
print(p1 == p2)
False
True
False
정리
객체 (Object) : 객체는 자신 고유의 속성(attribute)을 가지며 클래스에서 정의한 행위(behavior) 를 수행
클래스 (Class) : 공통된 속성(attribute)과 행위(behavior)를 정의한 것으로 객체지향 프로그램의 기본적인 사용자 정의 데이터형(user-defined data type)
인스턴스 (Instance) : 특정
class
로부터 생성된 해당 클래스의 실체/예시(instance)속성 (Attribute) : 클래스/인스턴스가 가지는 속성(값/데이터)
메서드 (Method) :클래스/인스턴스에 적용 가능한 조작법(method) & 클래스/인스턴스가 할 수 있는 행위(함수)
7.4 변수, 이름공간, 메서드¶
인스턴스 & 클래스 변수
인스턴스 & 클래스간의 이름공간
인스턴스 & 클래스 메서드(+ 스태틱 메서드)
7.4.1 인스턴스 & 클래스 변수¶
인스턴스 변수¶
인스턴스의 속성(attribute)
각 인스턴스들의 고유한 변수
생성자 메서드에서
self.변수명
로 정의인스턴스가 생성된 이후
인스턴스.변수명
로 접근 및 할당
활용법
class Person:
def __init__(self, name): # 인스턴스 메서드 (생성자)
self.name = name # 인스턴스 변수
# 위와 같은 Person 클래스를 똑같이 정의해봅시다.
class Person:
def __init__(self, name): # 인스턴스 메서드 (생성자)
self.name = name
def hi(self):
return 'hi'
p1 = Person('아이유')
p1.hi()
'hi'
# Person 클래스의 인스턴스 me, you를 각각 이름과 함께 생성하고, name 속성을 출력해봅시다.
me = Person('연희')
you = Person('지은')
me.name, you.name
('연희', '지은')
클래스 변수¶
클래스의 속성(attribute)
모든 인스턴스가 공유
클래스 선언 내부에서 정의
클래스.변수명
으로 접근 및 할당
활용법
class Circle:
pi = 3.14
print(Circle.pi)
# 위의 예시 코드대로 Circle 클래스를 생성해봅시다.
class Circle:
pi = 3.14
print(Circle.pi)
3.14
# Circle 클래스의 인스턴스 c1, c2를 생성해봅시다.
c1 = Circle()
c2 = Circle()
# 클래스 변수 pi에 접근해봅시다.
Circle.pi
3.14
# 인스턴스 c1, c2에서 pi 값을 출력해봅시다.
c1.pi, c2.pi
(3.14, 3.14)
# c1의 pi 값을 3.141592로 변경하여 봅시다.
c1.pi = 3.141592
# c1, c2에서의 pi값을 각각 출력해봅시다.
c1.pi, c2.pi
(3.141592, 3.14)
# Circle의 pi를 출력해봅시다.
print(Circle.pi)
3.14
7.4.2 인스턴스 & 클래스간의 이름공간¶
이름공간 탐색 순서¶
클래스를 정의하면, 클래스가 생성됨과 동시에 이름 공간(namespace)이 생성됩니다.
인스턴스를 만들게 되면, 인스턴스 객체가 생성되고 해당되는 이름 공간이 생성됩니다.
인스턴스의 어트리뷰트가 변경되면, 변경된 데이터를 인스턴스 객체 이름 공간에 저장합니다.
즉, 인스턴스에서 특정한 어트리뷰트에 접근하게 되면 인스턴스 => 클래스 순으로 탐색을 합니다.
class Person:
name = 'unknown'
# Person 클래스의 인스턴스 p1을 생성하고 name을 확인해봅시다.
p1 = Person()
p1.name
'unknown'
# p1의 name 속성을 직접 변경하고 확인해봅시다.
p1.name = 'Jack'
print(p1.name) #=> 인스턴스의 name
print(Person.name) #=> 클래스의 name
Jack
unknown
class와 instance는 서로 다른 namespace를 가지고 있습니다.
인스턴스에 해당 이름이 없으면 클래스에서 탐색을 합니다.
%%html
<iframe width="800" height="500" frameborder="0" src="https://pythontutor.com/iframe-embed.html#code=class%20Person%3A%0A%20%20%20%20name%20%3D%20'unknown'%0A%0Ap2%20%3D%20Person%28%29%0Aprint%28p2.name%29%0Ap2.name%20%3D%20'Jack'%0Aprint%28p2.name%29%0Aprint%28Person.name%29&codeDivHeight=400&codeDivWidth=350&cumulative=false&curInstr=0&heapPrimitives=nevernest&origin=opt-frontend.js&py=3&rawInputLstJSON=%5B%5D&textReferences=false"> </iframe>
7.4.3 메서드의 종류¶
인스턴스 메서드 (instance method)¶
인스턴스가 사용할 메서드
클래스 내부에 정의되는 메서드의 기본값은 인스턴스 메서드
호출시, 첫번째 인자로 인스턴스 자기자신
self
가 전달됩니다
활용법
class MyClass:
def instance_method(self, arg1, arg2, ...):
...
my_instance = MyClass()
# 인스턴스 생성 후 메서드를 호출하면 자동으로 첫 번째 인자로 인스턴스(my_instance)가 들어갑니다.
my_instance.instance_method(.., ..)
클래스 메서드 (class method)¶
클래스가 사용할 메서드
@classmethod
데코레이터를 사용하여 정의호출시, 첫 번째 인자로 클래스
cls
가 전달됩니다
활용법
class MyClass:
@classmethod
def class_method(cls, arg1, arg2, ...):
...
# 자동으로 첫 번째 인자로 클래스(MyClass)가 들어갑니다.
MyClass.class_method(.., ..)
스태틱 메서드 (static method)¶
클래스가 사용할 메서드
@staticmethod
데코레이터를 사용하여 정의호출시, 어떠한 인자도 전달되지 않습니다
활용법
class MyClass:
@staticmethod
def static_method(arg1, arg2, ...):
...
# 아무런 일도 자동으로 일어나지 않습니다.
MyClass.static_method(.., ..)
# MyClass 클래스를 정의해 두었습니다.
class MyClass:
def instance_method(self):
return self
@classmethod
def class_method(cls):
return cls
@staticmethod
def static_method(arg):
return arg
# MyClass 클래스의 인스턴스 mc를 생성해봅시다.
mc = MyClass()
mc.instance_method()
<__main__.MyClass at 0x1e400f6d610>
# 인스턴스 메서드를 호출하여 반환된 결과(self)와 인스턴스(mc)를 비교해봅시다.
# 1. id를 출력해 보고, 같은 id인지 확인
# 2. == 연산자를 확인해 비교
print(id(mc.instance_method()), id(mc))
print(mc.instance_method() == mc)
2078780347920 2078780347920
True
# 클래스 메서드를 호출하여 반환된 결과(cls)와 인스턴스(mc)를 비교해봅시다.
# 1. id를 출력해 보고, 같은 id인지 확인
# 2. == 연산자를 확인해 비교
print(id(MyClass.class_method()), id(MyClass))
print(MyClass.class_method() == MyClass)
2078768354784 2078768354784
True
# 스태틱 메서드를 호출하고 반환된 결과(arg)를 확인해봅시다.
# 어떠한 인자도 전달되는 것이 없습니다.
print(MyClass.static_method(1))
1
7.4.4 비교 정리¶
인스턴스와 메서드¶
인스턴스는 3가지 메서드 모두에 접근할 수 있습니다.
하지만, 인스턴스가 할 행동은 모두 인스턴스 메서드로 한정 지어서 설계합니다.
인스턴스에서 클래스 메서드와 스태틱 메서드는 되도록 호출하지 않아야 합니다. (가능하다 != 사용한다)
# 위의 MyClass를 활용하여 클래스 입장에서 확인해봅시다.
# 클래스 메서드를 호출해봅시다.
MyClass.class_method()
__main__.MyClass
# 스태틱 메서드를 호출해봅시다.
MyClass.static_method(1)
1
# 클래스로 인스턴스 메서드를 호출해봅시다.
# MyClass.instance_method(mc)는 mc.instance_method()와 같습니다.
print(MyClass.instance_method()) # Error => 첫 번째 인자인 인스턴스 객체가 없습니다.
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_9912/3496252957.py in <module>
2 # MyClass.instance_method(mc)는 mc.instance_method()와 같습니다.
3
----> 4 print(MyClass.instance_method()) # Error => 첫 번째 인자인 인스턴스 객체가 없습니다.
TypeError: instance_method() missing 1 required positional argument: 'self'
print(MyClass.instance_method(mc)) # mc.instance_method()와 같습니다.
<__main__.MyClass object at 0x000001E400F6D610>
클래스와 메서드¶
클래스가 할 행동은 다음 원칙에 따라 설계합니다. (클래스 메서드와 정적 메서드)
클래스 자체(
cls
)와 그 속성에 접근할 필요가 있다면 클래스 메서드로 정의합니다.클래스와 클래스 속성에 접근할 필요가 없다면 정적 메서드로 정의합니다.
정적 메서드는
cls
,self
와 같이 묵시적인 첫번째 인자를 받지 않기 때문
[코드예시] Puppy¶
Puppy 클래스의 속성에 접근하는 클래스 메서드를 생성해 봅시다.
클래스 변수
population
를 통해 강아지가 생길 때마다 증가 시키도록 하겠습니다.강아지들은 각자의 이름(
name
)과 종(breed
)을 갖고 있습니다.bark()
메서드를 호출하면 짖을 수 있습니다.
class Puppy:
population = 0 # class 변수
def __init__(self, name, breed): # 생성자 함수 (constructor)
self.name = name # instance 변수 초기화
self.breed = breed
Puppy.population += 1 # class 변수 값 변경
def __del__(self): # 소멸자
Puppy.population -= 1
def bark(self): # 인스턴스 메서드
print(f'왈왈! 나는 {self.name}, {self.breed}야') # 인스턴스 변수에 접근
@classmethod # 클래스 메서드
def get_population(cls):
print(f'현재 강아지 마리수: {cls.population}') # 클래스 변수에 접근
return cls.population
# Puppy 클래스의 인스턴스 p1, p2, p3를 생성해봅시다.
# name과 breed는 자유롭게 설정합니다.
p1 = Puppy('봄이', '사모예드')
p2 = Puppy('코코', '폼피츠')
p3 = Puppy('스노이', '말티즈')
# 각 인스턴스마다 bark 메서드를 실행해봅시다.
p1.bark()
p2.bark()
p3.bark()
왈왈! 나는 봄이, 사모예드야
왈왈! 나는 코코, 폼피츠야
왈왈! 나는 스노이, 말티즈야
# 클래스 메서드 get_population을 호출해봅시다.
Puppy.get_population()
현재 강아지 마리수: 3
3
# 위의 Puppy 클래스를 처음부터 정의해봅시다.
# 추가로 스태틱 메서드 info를 정의하고 호출해봅시다.
# info 메서드는 '이것은 Puppy 클래스입니다!' 라는 문자열을 return 합니다.
class Puppy:
population = 0
def __init__(self, name, breed):
self.name = name
self.breed = breed
Puppy.population += 1
def __del__(self):
Puppy.population -= 1
def bark(self):
print(f'왈왈! 나는 {self.name}, {self.breed}(이)야')
@classmethod
def get_population(cls):
print(f'현재 강아지 마리수: {cls.population}')
return cls.population
@staticmethod
def info():
return '이것은 Puppy 클래스입니다!'
d = Puppy('초코', '푸들')
# instance method
d.bark()
# static method
print(d.info(), Puppy.info())
# class method
print(Puppy.get_population())
왈왈! 나는 초코, 푸들(이)야
이것은 Puppy 클래스입니다! 이것은 Puppy 클래스입니다!
현재 강아지 마리수: 1
1
7.5 상속¶
상속 (Inheritance)
상속(Inheritance)
메서드 오버라이딩(Method Overriding)
다중 상속(Multiple Inheritance)
7.5.1 상속 (Inheritance) 이란?¶
클래스에서 가장 큰 특징은 상속
이 가능하다는 것입니다.
부모 클래스의 모든 속성이 자식 클래스에게 상속 되므로 코드 재사용성이 높아집니다.
활용법
class ChildClass(ParentClass):
<code block>
# Person 클래스를 정의해 보겠습니다.
class Person:
population = 0
def __init__(self, name='사람'):
self.name = name
Person.population += 1
def talk(self):
print(f'반갑습니다. {self.name}입니다.')
# Person 클래스의 인스턴스 p1을 생성해봅시다.
# name 속성은 자유롭게 설정합니다.
p1 = Person('아이유')
p1.talk()
print(Person.population)
반갑습니다. 아이유입니다.
1
# Person 클래스를 상속받아 Student 클래스를 만들어 보겠습니다.
class Student(Person):
def __init__(self, student_id, name='학생'):
self.name = name
self.student_id = student_id
Person.population += 1
# Student 클래스의 객체 s1을 만들어봅시다.
s1 = Student(1001, '배수지')
# s1의 name과 student_id를 확인해봅시다.
s1.name, s1.student_id
('배수지', 1001)
# 자식 클래스의 인스턴스는 부모 클래스에 정의된 메서드를 호출 할 수 있습니다.
# talk 메서드를 호출해봅시다.
s1.talk()
반갑습니다. 배수지입니다.
이처럼 상속은 공통된 속성이나 메서드를 부모 클래스에 정의하고, 이를 상속받아 다양한 형태의 사람들을 만들 수 있습니다.
issubclass(class, classinfo)
¶
class가 classinfo의 subclass면 True
isinstance(object, classinfo)
¶
object가 classinfo의 인스턴스거나 subclass인 경우 True
# issubclass 함수를 통해 Student 클래스와 Person 클래스가 상속관계인지 확인해봅시다. (클래스 상속 검사)
# issubclass(자식클래스, 부모클래스)
issubclass(Student, Person)
True
# isinstance 함수를 통해
# s1이 Student 클래스의 인스턴스인지,
# s1이 Person 클래스의 인스턴스인지 모두 확인해봅시다.
# isinstance(인스턴스, 클래스)
isinstance(s1, Student), isinstance(s1, Person)
(True, True)
# 내장 자료형들도 아래와 같이 상속 관계가 있습니다.
print(issubclass(bool, int)) # True
print(issubclass(float, int)) # False
True
False
super()
¶
자식 클래스에 메서드를 추가로 구현할 수 있습니다.
부모 클래스의 내용을 사용하고자 할 때,
super()
를 사용할 수 있습니다.
활용법
class ChildClass(ParentClass):
def method(self, arg):
super().method(arg)
# Person 클래스와 Student 클래스를 함께 정의해 보겠습니다.
class Person:
def __init__(self, name, age, number, email):
self.name = name
self.age = age
self.number = number
self.email = email
def greeting(self):
print(f'안녕, {self.name}')
class Student(Person):
def __init__(self, name, age, number, email, student_id):
self.name = name
self.age = age
self.number = number
self.email = email
self.student_id = student_id
p1 = Person('홍교수', 200, '0101231234', 'hong@gildong')
s1 = Student('학생', 20, '12312312', 'student@naver.com', '190000')
# p1과 s1 모두 greeting 메서드를 호출해봅시다.
p1.greeting()
s1.greeting()
안녕, 홍교수
안녕, 학생
위의 코드는 상속을 했음에도 불구하고 초기화(__init__
)에서 동일한 코드가 반복됩니다.
초기화의 중복을 super()
함수를 통해 제거해봅시다.
class Person:
def __init__(self, name, age, number, email):
self.name = name
self.age = age
self.number = number
self.email = email
def greeting(self):
print(f'안녕, {self.name}')
class Student(Person):
def __init__(self, name, age, number, email, student_id):
# Person 클래스
super().__init__(name, age, number, email)
self.student_id = student_id
p1 = Person('홍교수', 200, '0101231234', 'hong@gildong')
s1 = Student('학생', 20, '12312312', 'student@naver.com', '190000')
p1.greeting()
s1.greeting()
안녕, 홍교수
안녕, 학생
[실습] Rectangle & Square¶
아래의 조건에 만족하는 클래스 Rentangle
을 작성하세요.
Rectangle 클래스는 아래와 같은 속성과 메서드를 갖습니다.
인스턴스 속성
width
: 가로 길이height
: 세로 길이
인스턴스 메서드
area
: 직사각형의 넓이를 리턴perimeter
: 직사각형의 둘레의 길이를 리턴
# Rectangle 클래스를 정의해봅시다.
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property # 추가적인 인자가 없는 method에만 property를 달 수 있습니다.
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
# Rectangle 클래스의 인스턴스 rectangle을 가로 길이 4, 세로 길이 8로 초기화하고
# area 메서드와 perimeter 메서드를 통해 넓이와 둘레를 구해봅시다.
rectangle = Rectangle(4, 8)
rectangle.area(), rectangle.perimeter()
(32, 24)
Rectangle 클래스를 상속받아 Sqaure 클래스를 만들어 주세요.
Square 클래스는 Rectangle 클래스에서 상속받은 속성 외 추가 속성을 가지고 있지 않습니다.
단, 정사각형이므로 인스턴스 생성시 인자로 한 변의 길이만 받습니다.
# Square 클래스를 정의해봅시다.
class Square(Rectangle):
def __init__(self, length):
super().__init__(length, length)
# Square 클래스의 인스턴스 square를 길이 4로 초기화하고
# area 메서드와 perimeter 메서드를 통해 넓이와 둘레를 구해봅시다.
square = Square(4)
square.area(), square.perimeter()
(16, 16)
7.5.2 메서드 오버라이딩¶
Method Overriding(메서드 오버라이딩): 자식 클래스에서 부모 클래스의 메서드를 재정의하는 것
상속 받은 메서드를
재정의
할 수도 있습니다.상속 받은 클래스에서 같은 이름의 메서드로 덮어씁니다.
class Person:
def __init__(self, name, age, number, email):
self.name = name
self.age = age
self.number = number
self.email = email
def talk(self):
print(f'안녕, {self.name}')
# 위의 Person 클래스를 상속 받아 군인답게 말하는 Soldier 클래스를 만들어 보겠습니다.
# talk 메서드를 재정의(override) 하겠습니다.
class Soldier(Person):
def __init__(self, name, age, number, email, army):
super().__init__(name, age, number, email)
self.army = army
# method overriding
def talk(self):
print(f'충성! {self.army} {self.name}')
p = Person('일반인', 10, '010123', '1banin@gmail.com')
p.talk()
안녕, 일반인
s = Soldier('굳건이', 25, '0101234', 'soldier@roka.kr', '하사')
s.talk()
충성! 하사 굳건이
7.5.3 상속관계에서의 이름공간¶
기존의
인스턴스 -> 클래스
순으로 이름 공간을 탐색해나가는 과정에서 상속관계에 있으면 아래와 같이 확장됩니다.인스턴스 -> 자식 클래스 -> 부모 클래스
[연습] Person & Animal (메서드 오버라이딩)¶
사실 사람은 포유류입니다.
Animal Class를 만들고, Person Class 가 상속받도록 구성해봅시다.
(변수나, 메서드는 자유롭게 만들어보세요.)
예시)
모든 동물은 이름이 있고, 사람은 이름과 이메일이 있습니다.
모든 동물은 talk 메서드가 있습니다.
동물은 '으르렁'하고, 사람은 '안녕'합니다.
# 아래에 코드를 작성해주세요.
class Animal:
def __init__(self, name):
self.name = name
def talk(self):
print('grr')
class Person(Animal):
def __init__(self, name, email):
super().__init__(name)
self.email = email
def talk(self):
print('안녕')
animal = Animal('곰')
animal.talk()
person = Person('김싸피', 'hello@ssafy.com')
person.talk()
grr
안녕
7.5.4 다중 상속¶
두개 이상의 클래스를 상속받는 경우, 다중 상속이 됩니다.
상속 받은 모든 클래스의 요소를 활용 가능
중복된 속성이나 메서드가 있는 경우 상속 순서에 의해 결정
# Person 클래스를 정의합니다.
# Person 클래스는 생성자에서 인스턴스 변수로 name을 설정합니다.
class Person:
def __init__(self, name):
self.name = name
def greeting(self):
return f'안녕, {self.name}'
# Mom 클래스를 정의합니다.
# Mom 클래스는 Person 클래스를 상속받으며, 클래스 변수로 gene을 갖습니다. 값은 'XX'입니다.
# Dad 클래스만의 인스턴스 메서드 swim을 자유롭게 정의해봅시다.
class Mom(Person):
gene = 'XX'
def swim(self):
return '첨벙첨벙'
# Dad 클래스를 정의합니다.
# Dad 클래스는 Person 클래스를 상속받으며, 클래스 변수로 gene을 갖습니다. 값은 'XY'입니다.
# Dad 클래스만의 인스턴스 메서드 walk를 자유롭게 정의해봅시다.
class Dad(Person):
gene = 'XY'
def walk(self):
return '성큼성큼'
# FirstChild 클래스를 정의합니다.
# 상속의 순서가 중요합니다.(Dad, Mom) 순서로 상속받아봅시다.
# 상속받은 swim 메서드를 재정의(override)해봅시다.
# FirstChild 클래스만의 인스턴스 메서드 cry를 자유롭게 정의해봅시다.
class FirstChild(Dad, Mom):
def swim(self): # Mom의 swim 메서드를 오버라이딩 합니다.
return '챱챱'
def cry(self): # Child 만이 가지는 인스턴스 메서드 입니다.
return '응애'
# FirstChild 클래스의 인스턴스 baby1을 생성해봅시다.
baby1 = FirstChild('첫째')
# baby1의 cry 메서드를 실행해봅시다.
baby1.cry()
'응애'
# baby1의 swim 메서드를 실행해봅시다.
baby1.swim()
'챱챱'
# baby1의 walk 메서드를 실행해봅시다.
baby1.walk()
'성큼성큼'
# baby1의 gene 속성은 어떤 부모클래스의 속성값을 상속받는지 확인해봅시다.
baby1.gene
'XY'
# 이번에는 SecondChild 클래스를 만들어 상속 순서를 바꿔봅시다.
# (Mom, Dad) 순서로 상속받아봅시다.
# 상속받은 walk 메서드를 재정의(override)해봅시다.
# SecondChild 클래스만의 인스턴스 메서드 cry를 자유롭게 정의해봅시다.
class SecondChild(Mom, Dad):
def walk(self): # Dad 의 walk 메서드를 오버라이딩 합니다.
return '아장아장'
def cry(self):
return '응애'
# SecondChild의 인스턴스 baby2를 생성합니다.
baby2 = SecondChild('둘째')
# baby2의 cry 메서드를 실행합니다.
baby2.cry()
'응애'
# baby2의 walk 메서드를 실행합니다.
baby2.walk()
'아장아장'
# baby2의 swim 메서드를 실행합니다.
baby2.swim()
'첨벙첨벙'
# baby2의 gene 속성은 어떤 부모클래스의 속성값을 상속받는지 확인해봅시다.
baby2.gene
'XX'