goodthings4me.tistory.com
효율적 개발로 이끄는 파이썬 실천 기술 - 클래스와 인스턴스
(참고용으로 사용할 부분만 간략하게 정리)
파이썬의 클래스 구조
- 파이썬에서는 클래스도 객체이고, 클래스를 정의하면 그 이름의 클래스 객체가 만들어진다.
- class 키워드 사용하여 실행 시 객체를 만들어낸다.
- 같은 클래스의 여러 인스턴스(즉, 객체)는 같은 특성을 가지면서 서로 각각 독립된 상태를 유지한다.
# 클래스 정의 - Page라는 이름을 가진 객체(즉, 클래스 객체) 생성
# 이 클래스 객체는 그 자체로 새로운 인스턴스(객체)를 만들 수 있고,
# 인스턴스는 클래스 정의의 내용에 기술된 메서드나 변수를 가지는데,
# self.num 과 self.content 는 인스터스 변수이고, __init__() 와 output()는 인스턴스 메서드임
# 인스턴스 메서드의 첫 번째 인수에는 반드시 인스턴스 자신의 객체를 전달함(self)
class Page:
def __init__(self, num, content):
self.num = num # 페이지 번호
self.content = content # 페이지 내용
def output(self):
return f'(self.conttent)'
print(Page) # 클래스 객체 Page가 정의됨
print(type(Page)) # <class 'type'>
# type()은 인수로 전달받은 객체의 타입을 반환하는 기능과 클래스의 클래스 즉, 메타클레스(클래스 생성)임
# 예로,
print(type(int)) # <class 'type'>
a = 1
print(type(a)) # <class 'int'>
print(a.__class__) # <class 'int'>
print(a.__class__.__class__) # <class 'type'>
print(Page(1, 'my content')) # 인스터스 생성 및 초기화
page = Page(10, 'my content') # # 또 다른 인스터스 생성 및 초기화 후 변수 page에 대입(여기서 우리는 page를 보통 Page 클래스의 객체라 함)
print(page) # page가 가리키는 인스턴스 출력
print(type(page)) # # page가 가리키는 인스턴스의 클래스 타입 확인 => type(Page(2, 'my content'))
print(isinstance(page, Page)) # 클래스의 인스턴스인지 확인
[결과]
<class '__main__.Page'>
<class 'type'>
<class 'type'>
<class 'int'>
<class 'int'>
<class 'type'>
<__main__.Page object at 0x7f8d3408ae50>
<__main__.Page object at 0x7f8d3408afd0>
<class '__main__.Page'>
True
# 인스턴스화 연습
title_page = Page(0, 'Python Practice Book')
print(type(title_page)) # 인스턴스의 클래스를 확인함
print(isinstance(title_page, Page))
count = 1
for i in dir(title_page): # 인스턴스가 가진 속성 확인
print(i, end=', ')
count += 1
if count % 7 == 0:
print()
print()
print(title_page.__class__) # == print(type(title_page))
print(type(title_page.__class__)) # == print(type(Page))
[결과]
<class '__main__.Page'>
True
__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__, content,
num, output,
<class '__main__.Page'>
<class 'type'>
# 메서드 객체와 함수 객체
# 인스턴스 메서드와 함수는 기능상 차이 없고, 모두 function 클래스의 인스턴스로 구현될 수 있다
class Klass:
def some_method(self):
print('method')
print(type(Klass.some_method)) # <class 'function'>
def some_function(self):
print('function')
print(type(some_function)) # <class 'function'>
# 인스턴스 속성으로 클래스 객체에 연결된 함수를 참조(인스턴스를 통해 메서드에 접근)하면 그 함수는 인스턴스 메서드로 변환(method 클래스)
kls = Klass()
print(type(kls.some_method)) # <class 'method'>
# 파이썬은 실행 시 속성 검색을 수행하므로 인스턴스 메서드를 동적으로 추가할 수 있으나 가독성 또는 유지보수성이 떨어지므로 권장하지 않는다
Klass.some_function = some_function
print(kls.some_function())
[결과]
<class 'function'>
<class 'function'>
<class 'method'>
function
None
# 인스턴스 변수는 각 인스턴스가 독립적으로 가진다
title_page.section = 0
print(title_page.section)
first_page = Page(1, 'Python')
print(first_page.num)
#print(first_page.section) # AttributeError: 'Page' object has no attribute 'section'
[결과]
0
1
프로퍼티
- 프로퍼티(property) : 인스턴스 메서드를 인스턴스 변수처럼 다루는 기능
- @property 가 붙은 메서드는 값을 얻을 때 호출되기 때문에 getter 라고 하고,
- @메서드.setter 가 붙은 것은 값을 대입할 때 호출되기 때문에 setter 라고 함
- @property 가 붙은 인스턴스 메서드는 ()를 붙이지 않고도 호출할 수 있음
# 프로퍼티(property) 연습
class Book:
def __init__(self, raw_price):
if raw_price < 0:
raise ValueError('price must be positive')
self.raw_price = raw_price
self._discounts = 0 # _discounts는 private 변수
@property
def discounts(self): # getter
return self._discounts
@discounts.setter
def discounts(self, value): # setter
if value < 0 or 100 < value:
raise ValueError('discounts must be between 0 and 100')
self._discounts = value
@property
def price(self):
multi = 100 - self._discounts
return int(self.raw_price * multi / 100)
book = Book(2000)
print(book.discounts) # 초기 할인율 0
print(book.price) # 초기 가격
book.discounts = 20 # 할인율 설정, setter 호출
print(book.price) # 할인 후의 가격
#book.discounts = 120 # ValueError: discounts must be between 0 and 100
[결과]
0
2000
1600
# 언더스코어 두 개(__) 붙이면 name mangling 또는 name decoration
class Klass:
def __init__(self, x):
self.__x = x
kls = Klass(10)
#print(kls.__x) # AttributeError: 'Klass' object has no attribute '__x'
# name mangling __x 속성 변환 규칙 : __x => _Klass__x 변환을 통해 참조할 수 있으나 지양함
print(kls._Klass__x)
[결과]
10
클래스 변수
- 클래스에서 정의하는 것 : 인스턴스 변수, 인스턴스 메서드, 클래스 변수, 클래스 메서드
- 클래스 변수나 클래스 메서드는 클래스 객체에서 참조할 수 있고, 인스턴스에서도 참조 가능(해당 클래스의 모든 인스턴스에서 공유)
- 클래스 변수의 변경은 반드시 클래스 객체를 통해 대입해야 함 (인스턴스 통해 대입 시 클래스 변수는 변경되지 않고 그 인스턴스에만 존재하는 새로운 인스턴스 변수로 정의됨)
class Page:
book_title = 'Python Practice Book'
print(Page.book_title) # 클래스 객체로 접근
first_page = Page()
second_page = Page()
print(first_page.book_title) # 인스턴스로 클래스 변수 참조
print(second_page.book_title) # 클래스 변수는 모든 인스턴스에서 공유됨
Page.book_title = 'No title' # 클래스 변수 업데이트
print(Page.book_title)
print(first_page.book_title)
first_page.book_title = 'Python Book'
print(Page.book_title) # 클래스 변수 업데이트X
print(first_page.book_title) # 인스턴스 변수 book_title 의 값(클래스 변수와 같을 경우, 인스턴스 객체의 속성이 먼저 검색됨)
print(second_page.book_title) # 공유된 클래스 변수 계속 참조
[결과]
Python Practice Book
Python Practice Book
Python Practice Book
No title
No title
No title
Python Book
No title
클래스 메서드
- 클래스에 속한 메서드로, 첫 번째 인수(cls)에 클래스 객체를 전달함
- 데코레이터(@classmethod) 붙임
from operator import attrgetter
class Page:
book_title = 'Python Practice Book'
def __init__(self, num, content):
self.num = num
self.content = content
def output(self):
return f'{self.content}'
@classmethod
def print_pages(cls, *pages):
print(cls.book_title) # 클래스 객체 이용
pages = list(pages)
for page in sorted(pages, key=attrgetter('num')): # 페이지순으로 정렬해서 출력
print(page.output())
first = Page(1, 'first page')
second = Page(2, 'second_page')
third = Page(3, 'third_page')
Page.print_pages(first, third, second)
print('=' * 10)
first.print_pages(first, third, second)
[결과]
Python Practice Book
first page
second_page
third_page
==========
Python Practice Book
first page
second_page
third_page
클래스 상속
- class 클래스명(베이스 클래스) * 베이스 클래스 == 부모 클래스 또는 슈퍼 클래스
- 서브 클래스(또는 자식 클래스)에서는 베이스 클래스가 가진 메서드를 그대로 이용하면서, 새로운 메서드나 변수를 추가할 수 있다
- 필요에 따라 베이스 클래스가 가진 메서드를 덮어쓸 수 있다(메서드 오버라이드)
# 메서드 오버라이드와 super()를 사용한 베이스 클래스로의 접근 (베이스 클래스의 메서드 호출 시 내장함수 super() 이용)
# super()의 반환 객체는 프록시 객체(proxy object)이며, 그 클래스 자체는 아니다
class Page:
def __init__(self, num, content):
self.num = num
self.content = content
def output(self):
print('Page_output')
return f'{self.content}'
class TitlePage(Page):
def output(self): # 메서드 오버라이드
print('TitlePage_output')
title = super().output() # 명시적으로 호출해야 함
return title.upper()
title = TitlePage(0, 'Python Book')
title.output() # 호출되는 순서는 출력 결과를 보고 확인!!
[결과]
TitlePage_output
Page_output
PYTHON BOOK
[참고/출처] 효율적 개발로 이끄는 파이썬 실천 기술 - 파이썬에 숨겨진 힘을 이용해 개발 효율을 높이자!
'코딩 연습 > 코딩배우기' 카테고리의 다른 글
파이썬 실천 기술 #06 - 내장함수와 특수메서드(스페셜 메서드) (0) | 2021.06.12 |
---|---|
파이썬 실천 기술 #05 - 이름공간, 스코프 (0) | 2021.06.03 |
파이썬 실천 기술 #03 - 함수 (0) | 2021.05.25 |
파이썬 실천 기술 #02 - 데이터 구조 (0) | 2021.05.20 |
파이썬 실천 기술 #01 - PEP20, 제어흐름(if, for, while, Exception) (0) | 2021.05.18 |
댓글