[Zerobase][Python Mid] 클래스

솔비·2023년 11월 29일
0

💻 Python. w/zerobase

목록 보기
10/33
post-thumbnail

파이썬중급 [클래스]

1. 객체지향 프로그램

객체를 이용한 프로그램
객체(object) = 속성(attribute) + 기능(Function)

객체는 클래스에서 생성되고,
클래스에서 객체를 무한생성 할 수 있다.
클래스에서 객체를 만드는 것을 생성자 호출이라고한다.

  • 장점 : 코드재사용, 모듈화에 좋다.

🌟 용어정리

  • 클래스 : 제품의 설계도
  • 객체(=인스턴스) : 설계도로 만든 제품
  • 속성 : 클래스 안의 변수 (init뒤 매개변수)
  • 매서드(=기능) : 클래스 안의 함수
  • 생성자 : 객체를 만들 때 실행되는 함수
  • 생성자호출 : 객체 생성

2. 클래스생성

위에서 Car 클래스를 생성하고
init으로 속성(=클래스안의 변수)를 만든다.
(init과 self는 무조건 따라오는것이라고 이해하면 쉽다) 매개변수가 있을경우 넣고 기능만 있을경우는 제외
self는 객체 생성 시 객체자체이다.
e.g.
maesrati 객체의 color = 매개변수 c
maesrati 객체의 length = 매개변수 l

아래에 클래스의 기능인 매서드를 만들어주고 (print_car_info, do_stop, do_start)
객체호출 후 객체.매서드를 입력하면 각 객체의 기능을 사용할 수 있다.

생성자호출

위 파이참 사진에서 Car('red',100) 이라는 객체를 만들었는데,
이를 생성자 호출이라고 한다.
이때 init이 자동호출된다.

init()

Car()안의 'red',100 는 클래스의 속성이다.
생성자를 호출하면 init이 자동호출되고,
init은 속성을 초기화, 즉
Car(self, c, l)의 c,l에 'red',100를 할당한다.


3. 객체의 속성 변경

class New_pc :

    def __init__(self,name,cpu,memory, ssd):
        self.name = name
        self.cpu = cpu
        self.memory = memory
        self.ssd = ssd

    def pc_info(self):
        print(f'self.name : {self.name}')
        print(f'self.cpu : {self.cpu}')
        print(f'self.memory : {self.memory}')
        print(f'self.ssd : {self.ssd}')

    def do_excel(self):
        print('Excel Run')

    def do_photoshop(self):
        print('photoshop Run')

위 클래스에서

new_2023 = New_pc('gram','i5','16G','128G')
new_2023.pc_info()

#self.name : gram
#self.cpu : i5
#self.memory : 16G
#self.ssd : 128G

new_2023이라는 객체를 생성했을 때,
ssd를 바꾸고싶다면

new_2023.ssd = '256G'
new_2023.pc_info()

#self.name : gram
#self.cpu : i5
#self.memory : 16G
#self.ssd : 256G

객체.속성 = 바꿀속성값 으로
쉽게 바꿀 수 있다.

📢 매개변수가 없는 클래스

class Calculator :

    def __init__(self):
        self.number1 = 0
        self.number2 = 0
        self.result = 0

    def add(self):
        self.result = self.number1 + self.number2
        return self.result

    def sub(self):
        self.result = self.number1 - self.number2
        return self.result
        
        
cal1 = Calculator()
cal1.number1 = 30
cal1.number2 = 20

print(cal1.add())		#50
print(cal1.sub())		#10

매개변수 즉 속성이 없는 클래스의 경우,
속성값을 임의로 0으로 잡고
추후 속성값 변경을 해준 뒤 매서드를 사용 할 수 있다.


4. 객체와 메모리

클래스에서 객체를 만들면 객체는 메모리에 저장되는데 변수는 메모리 주소를 저장하고 이를 이용해서 객체를 참고한다.

cal1 = Calculator()	#객체생성
# :객체는 메모리로 
# cal1은 객체의 메모리 주소 저장
cal2 = cal1
# cal2은 cal1의 메모리 주소 복사
#즉 하나의 객체에 대한 주소를 저장

아래 클래스를 통해 깊은복사와 얕은 복사에 대해 알아보자

class Temcls :

    def __init__(self,n,s):

        self.number = n
        self.string = s

    def print_cls_info(self):
        print(f'self.number : {self.number}')
        print(f'self.string : {self.string}')

A. 얕은복사

위의 예시처럼 객체주소를 복사하는것
객체자체가 복사되지 않는다.
업로드중..

tc1 = Temcls(10,'hello')
tc2 = tc1

위 클래스에 대해 객체를 만들고 tc1이라는 변수에 초기화 시킨 후
tc2라는 변수에 tc1을 초기화 시킨경우

tc1.print_cls_info()
    #self.number : 10
    #self.string : hello
tc2.print_cls_info()
	#self.number : 10
    #self.string : hello

tc2.number = 3.14
tc2.string = 'Bye'

tc1.print_cls_info()
    #self.number : 3.14
    #self.string : Bye
tc2.print_cls_info()
    #self.number : 3.14
    #self.string : Bye

tc2만 변경했을 뿐인데도 tc1도 변경된다.
이유는 둘은 하나의 객체에 대한 주소를 공유하고있기 때문에
tc2로 객체의 속성을 변경하면 그 주소에 대한 객체자체가 수정되므로
tc1의 객체도 수정되었다고 볼 수 있다.

B. 깊은복사

객체자체를 복사하는 것
또 하나의 객체가 만들어 진다.
업로드중..

깊은 복사에는 여러 방법이 있다.
import copy 모듈을 사용한 복사
list의 경우 for문을 사용한 복사
extend까지 깊은 복사에 해당한다.

class이므로 copy로 인한 깊은 복사를 보면

import copy

tc11 = Temcls(10,'hello')
tc22 = copy.copy(tc11)

tc11.print_cls_info()
    #self.number : 10
    #self.string : hello
tc22.print_cls_info()
    #self.number : 10
    #self.string : hello

tc22.number = 3.14
tc22.string = 'Bye'

tc11.print_cls_info()
    #self.number : 10
    #self.string : hello
tc22.print_cls_info()
    #self.number : 3.14
    #self.string : Bye

copy로 인해 둘은 서로 다른 객체의 주소를 갖고있다.
그러므로 tc22의 객체수정을 하여도
tc11의 객체는 수정되지 않는다.


5.클래스상속

클래스는 또 다른 클래스를 상속해서 내것처럼 사용할 수 있다.
업로드중..

A. 상위 클래스의 속성이 없는경우

class Normal_car:

    def drive (self) :
        print('[Normal_Car] drive() called')
    def back (self) :
        print('[Normal_Car] back() called')

class Turbo_car(Normal_car) :

    def turbo(self) :
        print('[Turbo_Car] Turbo() called')


car1 = Turbo_car()

car1.drive()
car1.back()
car1.turbo()
#[Normal_Car] drive() called
#[Normal_Car] back() called
#[Turbo_Car] Turbo() called

상속받으려는 클래스선언을 할 때,
()안에 상속 받을 클래스를 넣어주
상속받을 클래스의 기능을
상속받은 클래스에서도 사용 할 수 있다.

B. 상위클래스의 속성이 있는경우 super()

상위클래스의 속성값을 하위클래스에서도 초기화해줘야한다.
(그래야 상위클래스의 속성값을 쓸 수 있을테니까)

1.상위클래스명.__init__(self,상위클래스 속성 초기화값,상위클래스 속성 초기화값)
2. super().__init__(상위클래스 속성 초기화값,상위클래스 속성 초기화값)
class P_class:

    def __init__(self,p_num1,p_num2):
        print('P_class 실행')
        self.p_num1 = p_num1
        self.p_num2 = p_num2

    def add_cal(self):
        result = self.p_num1 + self.p_num2
        return result

class C_class(P_class) :        #P_class 상속

    def __init__(self,c_num1,c_num2):
        print('C_class 실행')
        # P_class.__init__(self,c_num1,c_num2)
        super().__init__(c_num1,c_num2)
        self.c_num1 = c_num1
        self.c_num2 = c_num2

    def sub_cal(self):
        result = self.p_num1 - self.p_num2
        return result

여기서

cls = C_class(30,20)
# C_class(30,20) : 생성자호출 -> __init__메서드 호출 -> #속성값 초기화
#                           -> super로 P_class 속성값도 초기화

print(cls.add_cal())        #P_class 속성값도 초기화 됐으므로 30,20의 add_cal 사용가능
#C_class 실행
#P_class 실행
#50

✔️ 포인트 1
C_class(30,20)로 생성자를 호출했고, 자동으로 C_class의 init이 호출되었다.
-> print('C_class 실행')
super로 상위클래스인 P_class의 init도 호출되었으므로
-> print('P_class 실행')
(기능의 경우 상속만 하면 바로 쓸 수 있으나 위의 경우 P_class 속성값을 초기화해줘야하므로 (그래야 쓸 수 있으므로) super로 초기화해줘야 쓸 수 있음)

✔️ 포인트2
super로 상위클래스인 P_class의 속성값을 c_num1,c_num2로 초기화 시켜주었다.
C_class(30,20)의 생성자 호출에 넣은 속성값이 P_class의 속성값이 되었고,
cls = C_class(30,20)
print(cls.add_cal())
상위클래스의 기능인 add를 쓸 수 있게됨과 동시에 생성자 호출에 넣은 값이 상위클래스에 초기화되어
30과 20에 대한 add_cal() 기능이 실행된것이다.
개복잡

백문이불여일견이므로 실습

📁 실습

#중간고사 클래스와 기말고사 클래스를 상속관계로 만들고
#각각의 점수를 초기화 하자
#또한 총점 및 평균을 반환하는 기능도 만들어보자

#중간고사 클래스와 기말고사 클래스를 상속관계로 만들고
#각각의 점수를 초기화 하자
#또한 총점 및 평균을 반환하는 기능도 만들어보자

class Mid_exam :

    def __init__(self,s1,s2,s3):
        self.mid_kor_exam = s1
        self.mid_eng_exam = s2
        self.mid_math_exam = s3

    def print_mid_score(self):
        print(f'mid_kor_exam : {self.mid_kor_exam}')
        print(f'mid_eng_exam : {self.mid_eng_exam}')
        print(f'mid_math_exam : {self.mid_math_exam}')


class End_exam (Mid_exam) :
    def __init__(self,s1,s2,s3,s4,s5,s6):
        super().__init__(s1,s2,s3)
        self.end_kor_exam = s4
        self.end_eng_exam = s5
        self.end_math_exam = s6

    def print_end_score(self):
        print(f'end_kor_exam : {self.end_kor_exam}')
        print(f'end_eng_exam : {self.end_eng_exam}')
        print(f'end_math_exam : {self.end_math_exam}')

    def get_total_score(self):
        total = self.mid_kor_exam + self.mid_eng_exam + self.mid_math_exam
        #하위클래스의 메서드에서 상위클래스의 속성값 가져올 수 있음
        total += self.end_kor_exam + self.end_eng_exam + self.end_math_exam
        return total

    def get_avg_score(self):
        return self.get_total_score()/6




te = End_exam(80,90,60,90,80,50)

print(f'총 합계 : {te.get_total_score()}')
print(f'평균 : {te.get_avg_score()}')

#총 합계 : 450
#평균 : 75.0

6. 다중상속

2개이상의 클래스를 상속
업로드중..

class Car01:
    def drive(self):
        print('전진')

class Car02:
    def back(self):
        print('후진')

class Car03:
    def fly(self):
        print('날아라')

class Car(Car01,Car02,Car03) :
    def __init__(self):
        pass

mycar = Car()

mycar.drive()
mycar.back()
mycar.fly()
#전진
#후진
#날아라

이처럼 상속받을 하위클래스()에 ,로 넣어주면 된다.

📢 다중상속을 남발하지 않도록 한다.
메서드의 이름이 중복될 수 있기때문


7. 오버라이딩

하위클래스에서 상위클래스의 메서드를 재정의한다.
업로드중..

class Robot :

    def __init__(self,c,h,w):
        self.color = c
        self.height = h
        self.weight = w

    def fire(self):
        print('총알발사')


class New_Robot(Robot) :

    def __init__(self,c,y,w):
        super().__init__(c,y,w)

    def fire(self):
    	#super().fire()
        print('레이저발사')



my_robot = New_Robot('red',2004,300)
my_robot.fire()
#레이저발사

이처럼 상위 클래스의 메서드 명을 하위클래스에서 그대로 적어주고

def fire(self)

내용을 수정하면 해당 메서드를 호출할 때
하위 클래스에서 수정된 내용으로 호출할 수 있다.

이때, 상위 클래스의 내용도 같이 출력하고 싶다면?

super().fire() 

= 상위클래스의 fire를 불러와주자
그렇다면 상위클래스의 값도 가져올 수 있다.

my_robot.fire()
#총알발사
#레이저발사

📁 실습

#삼각형의 넓이를 계산하는 클래스를 만들고 이를 상속하는 클래스에서 get_area()를 오버라이딩하자

class Triangle_area:

    def __init__(self,w,h):
        self.width = w
        self.height = h

    def print_triagle_info(self):
        print(f'width : {self.width}')
        print(f'height : {self.height}')

    def get_area(self):
        return self.width * self.height / 2


class Triangle_area_cm2 (Triangle_area):

    def __init__(self,w,h):
        super().__init__(w,h)

    def get_area(self):
        return str(super().get_area()) + 'cm2'
        #super로 상위클래스의 get_area 불러오기


ta = Triangle_area_cm2(7,5)

ta.print_triagle_info()
area = ta.get_area()
print(f'Triagle Area : {area}')
#width : 7
#height : 5
#Triagle Area : 17.5cm2

오버라이딩 하는 하위클래스에서 연산을 두번 할 필요는 없다.
super().get_area()를 불러와서 cm2를 붙여주기면 하면 되니까 !


8. 추상클래스

상위클래스에서 하위클래스에 메서드 구현을 강요한다.
업로드중..

즉, 상위클래스에서는 선언만 하고
하위클래스에서 메서드 기능에 대해 구현한다.
오버라이딩이 하위클래스에서 재정의라면
추상클래스는 하위클래서에서의 정의라고 볼 수 있겠다.

from abc import ABCMeta
from abc import abstractclassmethod

class Airplain(metaclass=ABCMeta):

	@abstractclassmethod
    def flight(self):
        pass

class New_Airplain(Airplain):
    def flight(self):
        print('날아라')


my_airplain = New_Airplain()
my_airplain.flight()

위의
class Airplain(metaclass=ABCMeta)
구현을 강요할 메서드위에 @abstractclassmethod
를 입력하면 하위클래스에서 메서드 정의를 하지 않았을 때
error를 발생시킬 수 있다.

업로드중..
업로드중..

📢 추상클래스를 쓰는이유
하위클래스에서 입맛에 맞게 수정해서 써 ~


Zero Base 데이터분석 스쿨
Daily Study Note
profile
Study Log

0개의 댓글