본문 바로가기

프로그래밍/Python

[요약] 파이썬(Python) - 클래스(Class) 사용법

[요약] 파이썬(Python) - 클래스(Class) 사용법


클래스 선언

클래스 사용

클래스 상속, 다중상속

클래스 속성, 메소드

클래스 상속, 다중상속

클래스 인스턴스 변수 




# 파이썬(Python) 클래스(Class)이 이해

# 클래스, 인스턴스 차이
# 네임스페이스는 객체를 인스턴스화 할 때 저장된 공간
# 클래스 변수는 직접사용 가능, 객체보다 먼저 생성
# 인스턴스 변수는 객체마다 별도 존재, 인스턴스 생성이후에 사용 가능
# 상속, 다중상속


# 클래스 선언
'''
class ClassName:
    함수1
    함수2
    함수3
 '''   

# 예1)
# 코딩규칙1 : 클래스명 선언시 첫 문자는 대문자로
class ExampleClass:
    pass        # 아무 내용이 없다는 의미로 오류 방지 처리

class UserInfo:
    # 속성 : 클래스가 보유할 데이터 정보로 이해하면 됨
    # 메소드 : 클래스가 실행해야 할 액션(Action)    
    def __init__(self, name):     # 클래스 생성자(constructor)
        self.name = name          # 클래스 속성 -> 인스턴스가 생성 되어야만 사용 가능

    def user_info_p(self):
        print('Name : ', self.name)


user1 = UserInfo('홍길동')
user1.user_info_p()         # Name : 홍길동
user2 = UserInfo('박나래')
user2.user_info_p()         # Name : 박나래

# id() 는 메모리 주소 정보를 반환
print(id(user1))    # 2050347813192
print(id(user2))    # 2050347813384

# 객체인스턴스.__dict__는 객체의 데이터 정보를 JSON 형태로 보여준다.
print(user1.__dict__)   # {'name': '홍길동'}
print(user2.__dict__)   # {'name': '박나래'}



# 예2) self 의 이해
class SelfTest():
    def func1():    # 함수인자에 self가 없으면 Class함수로 클래스이름으로 직접 호출해서 사용해야 한다
        print('func1 called')

    def func2(self):
        print('func2 called')
        print(id(self))

self_test = SelfTest()
#self_test.func1() # 오류
SelfTest.func1()  # 클래스 함수 직접 호출
self_test.func2() # 인스턴스에서 self 파라미터는 기본값으로 지정되므로 별도 지정하지 않는다.
SelfTest.func2(self_test) # 클래스에서 직접 호출할 때는 self 인스턴스를 지정해 주어야 한다


# 예3) 클래스 변수, 인스턴스 변수
class WareHouse:
    stock_num = 0   # 클래스변수 - 델파이, C, C++, 자바와 다른 부분임

    def __init__(self, name): # 클래스 생성자(constructor)
        self.name = name    # 인스턴스 변수
        WareHouse.stock_num += 1

    def __del__(self): # 클래스 소멸자(destructor)
        WareHouse.stock_num -= 1

user1 = WareHouse('Kim')    
user2 = WareHouse('Park')   
user3 = WareHouse('Lee')    

print(user1.__dict__) # {'name': 'Kim'}
print(user2.__dict__) # {'name': 'Park'}
print(user3.__dict__) # {'name': 'Lee'}
print(WareHouse.__dict__)
''' 출력된 클래스 딕셔너리
{'__module__': '__main__', 'stock_num': 3, '__init__': <function WareHouse.__init__ at 0x000001BCD8790318>, 
'__del__': <function WareHouse.__del__ at 0x000001BCD87903A8>, '__dict__': <attribute '__dict__' of 'WareHouse' objects>,
 '__weakref__': <attribute '__weakref__' of 'WareHouse' objects>, '__doc__': None}
'''

print(user1.name)   # Kim
print(user2.name)   # Park
print(user3.name)   # Lee

# 클래스 인스턴스가 변수를 인식할 때 클래스인스턴스 네임스페이스내 변수가 없으면 클래스의 전역 변수를 찾는다
print(user1.stock_num)  # 3
print(user2.stock_num)  # 3
print(user3.stock_num)  # 3   --- 클래스변수는 전역으로 공유되기 때문에 같은 값이 출력됨

del user1   # 객체 인스턴스 소멸
print(user2.stock_num)  # 2
print(user3.stock_num)  # 2   


#################### 클래스 상속 및 다중상속 ################

# 예4)
# 상속의 기본
# 슈퍼클래스(부모) 및 서브클래스(자식) --> 자식클래스는 부모클래스의 모든 속성 및 메소르를 사용 가능하다

# 라면 -> 속성(종류, 회사, 맛, 면, 종류, 이름)  : 부모

class Car:
    """Parent"""
    def __init__(self, CarType, Color):
        self.CarType = CarType
        self.Color = Color

    def show(self):
        return 'Car Class "Show Method!"'

class BmwCar(Car):
    """SubClass"""
    def __init__(self, car_name, CarType, Color):
        super().__init__(CarType, Color)    # Inherit
        self.car_name = car_name

    def show_model(self) -> None:
        return "Your BMW Car Name : %s" % self.car_name

class BenzCar(Car):
    """SubClass"""
    def __init__(self, car_name, CarType, Color):
        super().__init__(CarType, Color)    # Inherit
        self.car_name = car_name

    def show_model(self) -> None:
        return "Your Benz Car Name : %s" % self.car_name

    def show(self):  # super클래스에 존재하는 메소드와 같은 경우 부모의 메소드가 오버라이딩됨(대체)
        print(super().show())   # 부모 클래스의 메서드를 호출한다
        return 'Car Info : %s %s %s' % (self.car_name, self.CarType, self.Color)

# 일반사용

model1 = BmwCar('520d', 'sedan', 'red')
print(model1.Color)     # red   - Super.Color
print(model1.CarType)   # sedan - Super.CarType
print(model1.car_name)  # 520d  - self.car_name

print(model1.show())            # Car Class "Show Method!"
print(model1.show_model())      # Your BMW Car Name : 520d
print(model1.__dict__)          # {'CarType': 'sedan', 'Color': 'red', 'car_name': '520d'}


# 메소드 오버라이딩(Method Overriding)

model2 = BenzCar('220d', 'sub', 'black')
print(model1.show())            # Car Class "Show Method!"
print(model2.show())            # Car Info : 220d sub black

# 부모 메소드 호출

model3 = BenzCar('350d', 'sedan', 'silver')
print(model3.show())            
"""
Car Class "Show Method!"     - 부모 클래스의 메소드가 호출됨
Car Info : 350d sedan silver - 자식 클래스가 오버라이딩 한 메소드가 호출된다
"""

# 계층형 상속에 대한 표현(Inheritance)
print(BmwCar.mro()) # [<class '__main__.BmwCar'>, <class '__main__.Car'>, <class 'object'>]


# 예5) 다중상속, 상속이 많아지면 코드 가독성이 떨어지고 복잡성이 올라간다
class X():
    pass

class Y():
    pass

class Z():
    pass

class A(X, Y): # 클래스 X, Y를 다중 상속
    pass

class B(Y, Z):
    pass

class M(B, A, Z):
    pass

print(M.mro())  # [<class '__main__.M'>, <class '__main__.B'>, <class '__main__.A'>, <class '__main__.X'>, <class '__main__.Y'>, <class '__main__.Z'>, <class 'object'>]
print(A.mro())  # [<class '__main__.A'>, <class '__main__.X'>, <class '__main__.Y'>, <class 'object'>]