본문 바로가기
Flask

Flask - flask 이해를 위한 파이썬 객체 지향

by DGK 2021. 12. 1.

 

Flask 입문 수업을 듣고 중요한 내용을 정리했습니다.
개인 공부 후 자료를 남기기 위한 목적이므로 내용 상에 오류가 있을 수 있습니다.

 

Class & Object(클래스와 객체)

기본 개념(Class & Object)

아래의 예시코드를 통해 클래스(Class)와 객체(Object)의 개념을 공부하고자 한다.

 

class Figure:
    count = 0  # 클래스 속성 
 
    # 생성자(initializer)
    def __init__(self, width, height):
        # self.* : 인스턴스 속성 
        self.width = width
        self.height = height
        # 클래스 속성의 접근 예시
        Figure.count += 1
    
    def __del__(self):
        Figure.count -= 1
    
    # 메서드(인스턴스 메서드)
    def calc_area(self):
        return self.width * self.height
        
        
print(Figure.count)      # 클래스 속성 출력
figure1 = Figure(2, 3)   # 객체 생성
print(Figure.count)      # 클래스 속성 출력
figure2 = Figure(4, 5)   # 객체 생성
print(Figure.count)      # 클래스 속성 출력
print(figure1.width)     # 인스턴스 속성 출력
print(figure2.width)     # 인스턴스 속성 출력
del figure1              # 객체 삭제
print(Figure.count)      # 클래스 속성 출력
del figure2              # 객체 삭제
print(Figure.count)      # 클래스 속성 출력

 

*결과

0
1
2
2
4
1
0

 

 

*기본 개념

 

  1. class Figure: 에서 Figure가 클래스명이며, 유일한 이름을 지어줘야한다. 
  2. 또한, 클래스명은 PEP Coding Convention에 가이드된 대로 각 단어의 첫 문자를 대문자로 하는 CapWords 방식을 사용한다.
  3. 클래스의 선언은 class 클래스명: or class 클래스명(): or class 클래스명(object): 의 3가지 방법으로 가능하다.
  4. 클래스는 일반적으로 속성과 메서드로 구성된다.
  5. 속성은 인스턴스 속성과 클래스 속성으로 구분되고, 메서드는 인스턴스 메서드와 클래스 메서드 그리고 정적 메서드로 구분된다.
  6. 클래스의 모든 인스턴스 메서드는 항상 self를 인자로 받아야 한다. (클래스 메서드와 정적 메서드는 제외)
  7. 인스턴스 속성은 주로 __init__() 메서드 안에서 선언되며, 클래스 내부에서는 self.인스턴스속성명으로 호출하고 클래스 외부에서는 객체명.인스턴스속성명으로 호출(접근)된다.
  8. 클래스 속성은 클래스 내부의 인스턴스 메서드 밖에서 선언되며, 클래스 안팎에서 모두 클래스명.클래스속성명으로 호출(접근)된다.
  9. 참고로, 클래스 속성은 해당 클래스를 사용하는 모두에게 공유되는 속성 값이지만 인스턴스 속성은 각 객체마다 서로 다른 값을 가진다.
  10. __init__() 메서드는 생성자라고 하며, 객체가 생성될 때 자동으로 호출되는 메서드이다. (반드시 self를 인자로 받음)
  11. 반면에 __del__() 메서드는 객체가 삭제될 때 자동으로 호출되는 메서드이다. (반드시 sefl를 인자로 받음)

 

 

*참고 내용

 

  1. Class 안에서는 함수를 '메서드'라고 하며, 변수를 '속성'이라고 한다. 
  2. 인스턴스는 클래스로 만든 객체를 의미하며, 객체와 유사한 의미를 가지지만 분명한 차이점이 존재한다.
  3. 즉, 인스턴스라는 말은 특정 객체가 어떤 클래스의 객체인지를 관계위주로 설명할 때 사용하는 용어이다.
  4. 예를 들면, a = Car() 일 때 'a는 객체'라는 표현이 어울리며 'a는 Car의 인스턴스'라는 표현이 적합하다. ('a는 인스턴스'라는 표현과 'a는 Car의 객체'라는 표현은 어색함)
  5. 인스턴스 메서드 호출 시, 인스턴스 메서드의 self인자에는 항상 객체가 들어간다. (반드시 self인자를 넣어야 하는 이유)

인스턴스 메서드의 실행 매커니즘

 

 

 

instance method & class method & static method

인스턴스 메서드와 정적 메서드 비교

아래의 예시코드를 통해 인스턴스 메서드와 정적 메서드의 개념을 공부하고자 한다.

 

class Figure:
    # 생성자(initializer)
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    # 인스턴스 메서드
    def calc_area(self):
        Figure.is_square(2, 3)           # 정적 메서드 호출
        print(self.width * self.height)

    # 정적 메서드
    @staticmethod
    def is_square(rect_width, rect_height):
        # print(self.width)              : 에러 발생
        if rect_width == rect_height:
            print("정사각형이 될 수 있는 너비/높이입니다.")
        else:
            print("정사각형이 될 수 없는 너비/높이입니다.")
         
         
figure1 = Figure(2, 3)    # 객체 생성
figure1.is_square(5, 5)   # 정적 메서드 호출
Figure.is_square(4, 5)    # 정적 메서드 호출(일반적)   
figure1.calc_area()       # 인스턴스 메서드 호출

 

 

*결과

정사각형이 될 수 있는 너비/높이입니다.
정사각형이 될 수 없는 너비/높이입니다.
정사각형이 될 수 없는 너비/높이입니다.
6

 

 

*기본 개념

 

  1. 인스턴스 메서드는 객체를 생성해야만 호출할 수 있으며, 항상 메서드의 인자로 self를 가지고 있어야 한다.
  2. 인스턴스 메서드의 경우, 클래스 안에서는 self.인스턴스메서드명으로 호출되며 클래스 밖에서는 객체명.인스턴스메서드명으로 호출된다.
  3. 인스턴스 메서드는 정적 메서드, 클래스 메서드와는 달리 객체의 속성에 접근할 수 있다. (ex. print(self.width * self.height))
  4. 정적 메서드는 메서드 앞에 @staticmethod라는 Decorator를 붙여야 한다.
  5. 정적 메서드는 인스턴스 메서드와 달리 self 인자를 가지고 있지 않다.
  6. 정적 메서드는 객체와 독립적이며, 로직상 클래스 안에서 선언되는 것이다. (객체가 생성되어야만 호출되는 인스턴스 메서드와 구별)
  7. 즉, 정적 메서드는 클래스 안에 존재하지만, 클래스와 분리되어 독립적으로 선언된 함수라고 봐도 무방하며 호출 시에만 클래스명을 사용한다.
  8. 정적 메서드는 클래스 안팎에서 모두 클래스명.정적메서드명 or 객체명.정적메서드명으로 호출할 수 있지만 클래스명.정적메서드명을 일반적으로 사용한다.
  9. 정적 메서드는 객체의 속성에 접근할 수 없다.

 

 

인스턴스 메서드와 클래스 메서드 비교

아래의 예시코드를 통해 인스턴스 메서드와 클래스 메서드의 개념을 공부하고자 한다.

 

class Figure1:
    count = 0  # 클래스 변수
 
    # 생성자(initializer)
    def __init__(self, width, height):
        # self.* : 인스턴스변수
        self.width = width
        self.height = height
        # 클래스 변수 접근 예
        Figure1.count += 1
    
    # 인스턴스 메서드
    def calc_area(self):
        return self.width * self.height

    # 클래스 메서드
    @classmethod
    def print_count(cls):
        # print(self.width)    : 에러 발생
        return cls.count

figure1 = Figure1(2, 3)        # 객체 생성
figure2 = Figure1(4, 5)        # 객체 생성


print(Figure1.count)           # 클래스 속성 출력
print(Figure1.print_count())   # 클래스 메서드 호출
print(figure1.print_count())   # 클래스 메서드 호출
print(figure1.calc_area())     # 인스턴스 메서드 호출
print(figure2.calc_area())     # 인스턴스 메서드 호출

 

 

*결과

2

2

2

6

20

 

 

*기본 개념(클래스 메서드)

 

  1. 클래스 메서드는 메서드 앞에 @classmethod라는 Decorator를 붙여야 한다.
  2. 클래스 메서드는 self 인자 대신, cls 인자를 가진다. (cls 인자도 self 인자와 동일한 원리)
  3. 클래스 메서드는 클래스 속성에는 접근할 수 있지만(cls.클래스속성명), 객체의 속성/메서드에는 접근할 수 없다. (ex. print(self.width) : 에러 발생,  return cls.count : 에러 발생x)
  4. 클래스 메서드는 클래스 안팎에서 모두 클래스명.클래스메서드명 or 객체명.클래스메서드명으로 호출할 수 있다.

 

 

클래스 메서드와 정적 메서드 비교(심화)

아래의 두 예시코드를 통해 클래스 메서드와 정적 메서드의 개념을 공부하고자 한다.

 

class Figure:
    @classmethod
    def set_name(cls, name):
        cls.name = name

class Circle(Figure):
   pass
 
Figure.set_name("figure")
print(Figure.name, Circle.name)

Circle.set_name("circle")
print(Figure.name, Circle.name)

 

*결과

figure figure

figure circle

 

class Figure:
    @staticmethod
    def set_name(name):
        Figure.name = name

class Circle(Figure):
   pass
 
Figure.set_name("figure")
print(Figure.name, Circle.name)

Circle.set_name("circle")
print(Figure.name, Circle.name)

 

*결과

figure figure

circle circle

 

 

*기본 개념

 

  1. 클래스 메서드는 해당 클래스만을 범위로 가진다.
  2. 따라서 클래스 메서드를 상속받은 특정 클래스(Circle)에 새로운 인자(circle)가 들어와도, 기존 클래스(Figure)의 클래스 메서드는 영향을 받지 않는다.
  3. 정적 메서드는 클래스와 별개의 함수로 특정 범위가 존재하지 않는다.
  4. 따라서 정적 메서드를 상속받은 특정 클래스(Circle)에 새로운 인자(cirlce)가 들어오면, 기존 클래스(Figure)의 정적 메서드에도 새로운 인자가 들어가게 되는 것이다. 

 

 

Class Inheritance(상속)

추상화(abstraction)와 상속(inheritance)

추상화는 여러 클래스에 중복되는 속성, 메서드를 하나의 기본 클래스로 작성하는 작업을 의미한다.

상속은 기본 클래스의 공통 기능을 물려받고, 다른 기능을 추가 or 변경할 수 있는 작업을 의미한다.

 

이 때 기본 클래스는 부모 클래스(또는 상위 클래스, Parent 클래스, Super 클래스, Base 클래스)라고 하고, 기본 클래스의 기능을 물려받은 클래스는 자식 클래스(또는 하위 클래스, Child 클래스, Sub 클래스, Derived 클래스)라고 한다.

 

클래스의 상속은 부모 클래스를 자식 클래스에 인자로 넣으면 이루어진다.

클래스의 상속은 주로 코드의 재사용을 위해 사용되며, 상속받은 공통 기능의 경우에는 기본 클래스만 수정하면 된다는 장점이 존재한다.

참고로, 부모 클래스가 둘 이상인 경우에는 다중 상속이라 한다.

 

*ex. 도형 클래스와 사각형, 삼각형, 원 클래스 (이 중 Figure 클래스(도형 클래스)와 사각형 클래스만 예시로 설명)

 

공통점 : 도형의 이름, 도형의 색깔

차이점

  - 삼각형 : 삼각형 한 변 길이, 삼각형 넓이

  - 사각형 :  사각형 너비/높이, 사각형 넓이

  - 원 : 원 반지름, 원 넓이

 

아래의 예시코드를 통해, 상속의 개념을 자세히 공부해보자.

 

class Figure:
    def __init__(self, name, color):
        self.name = name
        self.color = color
        
        
class Quadrangle(Figure):
    def set_area(self, width, height):
        self.width = width 
        self.height = height

    def get_info(self):
        print(self.name, self.color, self.width * self.height)
      
      
square = Quadrangle('kim', 'blue')
square.set_area(5, 5)

square.get_info()

print(issubclass(Quadrangle, Figure))
print(isinstance(figure1, Figure))
print(isinstance(square, Figure))
print(isinstance(figure1, Quadrangle))
print(isinstance(square, Quadrangle))

 

*결과

kim, blue, 25

True

True

True

False

True

클래스 간 상속 구조

 

 

*참고 내용

 

  1. 자식(Quadrangle) 클래스는 부모(Figure) 클래스를 상속받았기 때문에, 해당 클래스의 인스턴스 속성은 4개(name, color, width, height)이고, 인스턴스 메서드는 총 3개(__init__, set_area, get_info)이다.
  2. square = Quadrangle('kim', 'blue')에서 'kim'과 'blue'는 Figure 클래스의 __init__() 메서드 인자인 name, color로 들어간다.
  3. square.set_area(5, 5)에서 두 개의 5는 Quadrangle 클래스의 set_area() 메서드 인자인 width, height으로 들어간다.
  4. issubclass() 함수는 특정 클래스가 어떤 클래스의 자식 클래스인지를 알려준다.
  5. isinstance() 함수는 특정 객체가 어떤 클래스와 관계가 있는지를 알려준다. (figure1 객체와 Quadrangle 클래스는 관계 x)

 

 

메서드 재정의(method override)

메서드 재정의(method override)는 부모 클래스의 method를 자식 클래스에서 재정의함으로써 부모 클래스의 메서드가 아닌 자식 클래스에서 재정의한 메서드를 호출할 수 있다. 참고로, 자식 클래스에서 부모 클래스의 메서드를 재정의할 경우 메서드의 이름이 동일해야 하며, 일부 언어(C++, Java)에서는 인자도 동일해야 한다.

 

아래의 예시코드를 통해, 메서드 재정의의 개념을 자세히 공부해보자.

 

class Person:
    def __init__(self, name):
        self.name = name

    def work(self):
        print(self.name + " works hard")        

class Student(Person):
    def work(self):
        print(self.name + " studies hard")
        

student1 = Student("kim")
student1.work()

 

*결과

kim studies hard

 

class Car:
    def __init__(self, name):  # 생성자
        self.name = name
    
    def get_info(self):
        print(self.name)

class ElecCar(Car):
    def get_info(self):
        print(self.name, 'Fuel: Eletronic')  # 메서드 override

        
class GasoCar(Car):
    def get_info(self):
        print(self.name, 'Fuel: Gasoline')   # 메서드 override

elec = ElecCar('kim')
gaso = GasoCar('james')
elec.get_info()
gaso.get_info()

 

*결과

kim Fuel: Eletronic
james Fuel: Gasoline

 

 

*참고내용

 

  1. 부모 클래스에서 정의된 메서드를 자식 클래스에서 재정의할 경우, 부모 클래스의 메서드는 호출되지 않고 새롭게 재정의한 메서드가 호출된다.
  2. 이를 메서드 재정의(method override)라고 한다.

 

 

자식 클래스에 새로운 메서드 추가 방법

아래의 예시코드를 통해, 자식 클래스에서 새로운 메서드를 추가하는 방법을 공부해보자.

 

class Person:
    def work(self):
        print('work hard')

class Student(Person):
    def work(self):
        print('Study hard')
        
    def go_to_school(self):    # 메서드 추가
        print('Go to school')
        
        
p1 = Person()
s1 = Student()

p1.work()
#p1.go_to_school()  : 에러 발생

s1.work()
s1.go_to_school()

 

*결과

work hard
Study hard
Go to school

 

*참고 내용

 

  1. 이처럼, 상속받은 자식 클래스에서 부모 클래스에는 없는 새로운 기능(메서드)을 추가할 수도 있다.
  2. 자식 클래스에서 새롭게 추가한 메서드는 부모 클래스에서 접근할 수 없다. (에러 발생)

 

 

자식 클래스에서 부모 클래스의 메서드를 호출하는 방법

아래의 예시코드를 통해, 자식 클래스에서 부모 클래스의 메서드를 호출하는 방법을 공부해보자.

 

class Person:
    def work(self):
        print('work hard')

class Student(Person):
    def work(self):
        print('Study hard')
        
    def parttime(self):
        super().work()    # 부모 클래스의 메서드 호출

    def general(self):
        self.work()       # 자식 클래스의 메서드 호출
        
        
student1 = Student()
student1.work()
student1.parttime()
student1.general()

 

*결과

Study hard
work hard
Study hard

 

 

*참고 내용

 

  1. super()는 자식 클래스에서 부모 클래스의 메서드를 호출할 때 사용하며, 'super().부모클래스메서드명'으로 코드를 작성한다.
  2. self는 원래 객체 자신을 의미하므로, 'self.자식클래스메서드명'으로 코드를 작성하여 자식 클래스의 메서드를 호출할 수 있다. (C언어와 Java는 this라는 키워드를 사용함)

 

 

댓글