Python의 class 개념에 대한 정리
[참고] 파이썬 - 기본을 갈고 닦자! (https://wikidocs.net/book/1553)
Class, Instance, Method, Attribute
# 메소드 작성하기
class Flight:
# 클래스 속성: class 안에서 바로 할당하는 속성 (__init__에서 할당했던 변수들은 모두 인스턴스 속성)
# 클래스 속성에서의 직접 접근, 객체에서의 접근할 때 모두 똑같은 값을 공유함
class_attr = []
# 생성자(constructor)로 객체 생성을 호출받으면 먼저 __new__를 호출하여 객체를 생성할당하고, __new__ 메소드가 __init__ 메소드를 호출하여 객체에서 사용할 초기값 초기화
# __init__(): 메소드만 오버라이딩하여 객체초기화에만 이용할 뿐 생성자는 아님
def __init__(self, number):
# super().__init__()
# 초기화자(__init__)에 객체의 불변성 확립 (유효성 검증 수행)
# 객체 생성시 들어올 값에 대해 __init__에서 validation 수행
# e.g. 비행기의 번호는 앞 두글자는 영문이어야하고 대문자이며, 뒤 세번째 글자부터 마지막까지는 양의 정수
# 객체 생성시 규칙에 맞지 않는 값이 들어오면 ValueError 발생시키기
if not number[:2].isalpha():
raise ValueError("첫 두글자가 알파벳이 아닙니다.")
if not number[:2].isupper():
raise ValueError("첫 두글자가 대문자가 아닙니다.")
if not number[2:].isdigit():
raise ValueError("세 번째 글자 이상이 양의 숫자가 아닙니다.")
# `_` 네이밍 컨벤션 (_single_leading_underscore: 내부적으로 사용되는 변수)
# [참고] Python은 기본적으로 다른 언어와 달리 접근 제어자(public, private, protected)가 없고, 모두 public (생성된 인스턴스에서 _number 변수 접근 가능)
# self._number = number
# [참고] 변수 왼쪽에 __(더블스코어)를 붙인 경우 (Mangling): 선언된 클래스 안에서만 해당 이름으로 사용 가능
# 외부에서 접근이 불가능하지만, `_클래스명__변수명` 형식으로는 접근 가능
# e.g. >>> f = Flight("AB001")
# >>> print(f._Flight__number)
# 'AB001'
self.__number = number
# 객체 생성 시 __new__가 클래스 자체를 받으며 할당하게 되고, __init__가 self를 받으며 객체의 내부에서 사용할 속성 초기화
# __new__ 메소드는 자동으로 실행되므로 굳이 작성하지 않아도 됨
# def __new__(cls):
# print('new')
# return super().__new__(cls)
# 인스턴스 메소드: 객체에서 호출되어질 수 있는 함수
# 파이썬 메소드의 첫 번째 파라미터명은 관례적으로 self라는 이름 사용
# 호출 시 호출한 객체 자신이 전달되기 때문에 self라는 이름 사용 (메소드의 첫 번째 인자로 항상 인스턴스(객체)가 전달)
# 이를 이용하여 클래스에서 바로 메소드로 접근하면서 위에서 할당한 Flight의 객체 f를 파라미터로 전달해도 똑같은 결과값 얻음
def number(self):
return self.__number
def add_class_attr(self, number):
Flight.class_attr.append(number)
class Fly:
# 비공개 클래스 속성: 언더바 두 개(__)로 시작 (비공개 속성으로 만들기)
__private_attr = 5
class_attr = []
# 클래스 속성과 인스턴스 속성을 동일하게 만들면, 인스턴스 속성을 먼저 찾게됨
# [참고] 속성과 메소드 이름 찾는 순서: 인스턴스 -> 클래스
# [주의] 클래스 속성은 여러 객체가 공유함
def __init__(self):
self.class_attr = []
def add_instance_attr(self, number):
self.class_attr.append(number)
def add_class_attr(self, number):
Fly.class_attr.append(number)
Method Overloading (Python에서 불가능)
"""
파이썬은 메소드 오버로딩 불가능
(아래의 클래스의 show 메소드는 가장 밑에 있는 것으로 동작)
메소드 오버로딩: 하나의 클래스 내부에서 메소드 명칭은 똑같고, 인자를 다르게하는 형태 (Java에서는 가능)
"""
class Korea:
def __init__(self, name, population, capital):
self.name = name
self.population = population
self.capital = capital
def show(self):
print(
"""
국가의 이름은 {} 입니다.
국가의 인구는 {} 입니다.
국가의 수도는 {} 입니다.
""".format(self.name, self.population, self.capital)
)
def show(self, abc):
print('abc :', abc)
Inheritance
"""
상속(inheritance)
- 물려주는 클래스(Parent Class, Super Class)의 내용(속성과 메소드)을 물려받는 클래스(Child Class, Sub Class)가 가지게 되는 것
e.g. 국가 클래스 -> 한국, 일본, 중국, 미국 클래스
- 국가 클래스가 속성으로 인구를 만들었으면, 상속 받은 한국, 일본, 중국, 미국 클래스에서는 부모 클래스의 속성인 인구를 비롯하여 메소드 사용 가능
자식 클래스를 선언할 때 소괄호로 부모 클래스 포함하면, 자식 클래스에서는 부모 클래스의 속성과 메소드 사용 가능
"""
class Country:
"""Super Class"""
name = '국가명'
population = '인구'
capital = '수도'
def show(self):
print('국가 클래스의 메소드입니다.')
class Province:
Province_list = []
def show(self):
print('province show method!')
# 상속받은 서브 클래스에서는 상속해준 슈퍼 클래스의 속성과 메소드 모두 사용 가능
# 파이썬은 다중 상속 가능 (C#, Java는 불가능, C++은 가능)
class Korea(Country, Province):
"""Sub Class"""
def __init__(self, name, population, capital):
self.name = name
self.population = population
self.capital = capital
# 메소드 오버라이딩 (Method Overriding): 부모 클래스의 메소드를 자식 클래스에서 재정의
# 부모 클래스의 메소드는 무시되고 자식 클래스의 메소드가 수행
def show(self):
# super() 사용시 자식 클래스 내에서 부모 클래스 호출 가능
super().show()
print(
"""
국가의 이름은 {} 입니다.
국가의 인구는 {} 입니다.
국가의 수도는 {} 입니다.
""".format(self.name, self.population, self.capital)
)
def show_name(self):
print('국가 이름은 : ', self.name)
Static Method & Class Method
"""
정적 메소드 (@classmethod, @staticmethod)
- 정적 메소드: 클래스에서 직접 접근할 수 있는 메소드
- [주의] 파이썬에서는 다른 언어와 다르게 정적 메소드임에도 불구하고 인스턴스에서도 접근 가능
"""
class CustomClass:
# instance method는 첫 번째 인자로 객체 자신 self를 입력
def add_instance_method(self, a, b):
return a + b
# classmethod는 첫 번째 인자로 클래스를 입력
@classmethod
def add_class_method(cls, a, b):
return a + b
# staticmethod는 특별히 추가되는 인자가 없음
@staticmethod
def add_static_method(a, b):
return a + b
"""
정적 메소드
- 일반적으로 인스턴스 데이터를 액세스할 필요가 없는 경우 클래스 메소드 혹은 정적 메소드 사용
- 클래스 변수를 액세스할 필요가 있을 때 클래스 메드 사용
- 클래스 변수를 액세스할 필요가 없을 때 정적 메소드 사용
@classmethod와 @staticmethod의 차이
staticmethod에서는 부모 클래스의 클래스 속성 값을 가져오지만,
classmethod에서는 cls 인자를 활용하여 cls의 클래스 속성을 가져옴
"""
class Language:
default_language = "English"
def __init__(self):
self.show = '나의 언어는' + self.default_language
# 클래스 메소드(classmethod): cls라는 클래스를 의미하는 파라미터 전달 받아서, 클래스 변수 등에 액세스 가능
#
@classmethod
def class_my_language(cls):
return cls()
# 정적 메소드(staticmethod): self 파라미터를 갖지 않고 인스턴스 변수에 액세스할 수 없음
# 보통 객체 필드와 독립적이지만 로직상 클래스 내에 포함되는 메소드에 사용
@staticmethod
def static_my_language():
return Language()
def print_language(self):
print(self.show)
class KoreanLanguage(Language):
default_language = "한국어"
"""
정적 메소드 사용 예제
- 인스턴스 데이터 액세스 필요 없을 때: class method, static method 사용
- 클래스 변수 액세스 필요 있을 때: class method 사용
- 클래스 변수 액세스 필요 없을 때: static method 사용
"""
class Rectangle:
# 클래스 변수
count = 0
def __init__(self, width, height):
self.width = width
self.height = height
Rectangle.count += 1
# instance method
def calc_area(self):
area = self.width * self.height
return area
# static method
@staticmethod
def is_square(rectWidth, rectHeight):
return rectWidth == rectHeight
# class method
@classmethod
def print_count(cls):
print(cls.count)
Abstract Class
"""
추상 클래스(abstract class)
- 미구현 추상 메소드를 한 개 이상 가지며, 자식 클래스에서 해당 추상 메소드를 반드시 구현하도록 강제
- 상속받은 클래스는 추상 메소드를 구현하지 않아도, import 할 때까지는 에러 발생하지 않으나 객체 생성 시 에러 발생
- 반드시 `abc` 모듈 import 필요
- 추상메소드를 추가한 후에는 해당 클래스로 객체를 생성하면 에러 발생
e.g.
from abc import *
class 추상클래스명(metaclass=ABCMeta):
@abstractmethod
def 추상메소드(self):
pass
"""
from abc import *
class AbstractCountry(metaclass=ABCMeta):
name = '국가명'
population = '인구'
capital = '수도'
def show(self):
print('국가 클래스의 메소드입니다.')
# 추상메소드 추가
@abstractmethod
def show_capital(self):
print('국가의 수도는?')
class Korea(AbstractCountry):
def __init__(self, name, population, capital):
self.name = name
self.population = population
self.capital = capital
def show_name(self):
print('국가 이름은 : ', self.name)
# 상속받은 추상메소드 구현(필수)
def show_capital(self):
super().show_capital()
print(self.capital)
Duck Typing
"""
Duck Typing
'If it walks like a duck and it quacks like a duck, then it must be a duck'
'오리처럼 걷고, 오리처럼 꽥꽥거리면, 그것은 틀림없이 오리다.'
- 본질적으로 다른 클래스라도 객체의 적합성은 객체의 실제 유형이 아니라 특정 메소드와 속성의 존재에 의해 결정됨
"""
class Parrot:
def fly(self):
print("Parrot flying")
class Airplone:
def fly(self):
print("Airplane flying")
class Whale:
def swim(self):
print("Whale swimming")
# Parrot 클래스와 Airplane 클래스는 서로 상속 등의 관계가 없으나, 내부에 동일한 메소드인 fly()가 있기 때문에, lift_off 함수 호출 시 fly 정상 실행
# 반면, Whale 클래스는 fly() 메소드가 없기 때문에, AttributeError 발생
def lift_off(entity):
entity.fly()
parrot = Parrot()
airplane = Airplone()
whale = Whale()
lift_off(parrot) # prints "Parrot flying"
lift_off(airplane) # prints "Airplane flying"
lift_off(whale) # Throws the error `'Whale' object has no attribute 'fly'`