[TIL 관련] - 3주차 - 파이썬 클래스

dong-il·2022년 6월 2일
0

SFS-프로젝트

목록 보기
4/5
post-thumbnail

Intro

객체지향 프로그래밍(Object Oriented Programming) : 객체를 우선으로 생각해서 프로그래밍한다는 의미

3주차에 "스마트스토어 상품 자동 등록 자동화 프로그램"프로젝트 리팩토링을 계획했다. 리팩토링을 하기전에 기존 코드가 객체지향 언어인 파이썬으로 작성되었지만 내부 동작은 절차지향으로 되었기 때문에 OOP(Object Oriented Programming)도 같이 적용하기로 했다. 그래서 우선적으로 파이썬 클래스에 대해 공부하는 시간을 가지기로 했다. (참고로 OOP에 대해서도 공부하는 시간을 가지기로 함.)

클래스

클래스(Class) : 객체지향 프로그래밍의 기본 단위

1. 클래스의 기본

객체

객체(Object) : 여러 가지 속성을 가질 수 있는 대상

클래스 선언

  • 클래스 구조

    class 클래스 이름:
    	클래스 내용
    • 클래스의 이름은 기존 함수에 사용하는 스네이크 케스와는 다르게 캐멀 케이스(파스칼 케이스) 규칙을 지켜 작성!
  • 인스턴스 생성

    인스턴스 이름(변수 이름) = 클래스 이름()
    • 인스턴스(instance) : 클래스를 기반으로 만들어진 객체
    • 클래스 이름() 👈🏻 생성자 함수라고 함.

생성자

생성자(Constructor) : 클래스 이름과 같은 함수

  • 클래스 내부에 __init__라는 함수를 만들어 객체를 생성할 때 객체를 초기화 시킴.
class 클래스 이름:
	def __init__(self, 추가적인 매개변수):
    	pass
  • 클래스 내부의 함수(메소드)는 첫 번째 매개변수로 반드시! self를 입력해야함.
  • self : '자기 자신'을 나타내는 딕셔너리라고 생각하면 됨. 단, self가 가지고 있는 속성과 기능에 접근할 때는 self.<식별자> 형태로 접근! (참고로 self는 키워드가 아닌 단순 식별자)
  • __init__이라는 생성자와 반대로 인스턴스가 소멸될 때 호출되는 함수도 있음 👉🏻 소멸자(Destructor)라고 하고 __del__을 사용.

메소드

메소드 : 클래스가 가지고 있는 함수

class 클래스 이름:
	def 메소드 이름(self, 추가적인 매개 변수):
    	pass
  • C#이나 Java 등의 언어에서는 '메소드'라는 단어를 많이 사용하지만 파이썬에서는 멤버 함수 혹은 인스턴스 함수 등의 용어를 많이 사용함!

2. 클래스의 추가적인 구문

isinstance(인스턴스, 클래스)

  • 객체(인스턴스)가 어떤 클래스로부터 만들어졌는지 확인
  • 리턴값은 True, False
# 클래스 선언
class Student:
	def __init__(self):
    	pass

# 학생 선언
student = Student()

# 인스턴스 확인
print("isinstance(student, Student) : ", isinstance(student, Student))

결과값

isinstance(student, Student) : True
  • Tip
    • 단순한 인스턴스 확인의 경우 type(student) == Student를 사용가능!
    • but '상속'을 사용할 때는 다르게 동작하기 때문에 상속된 클래스의 경우 isinstance()를 사용해야함.

특수한 이름의 메소드

__<이름>__() 형태의 메소드는 특수한 상황에 자동으로 호출되도록 만들어짐.

  • __str__()클래스를 내부 정의하면 str()함수를 호출할 때 __str__()함수가 자동으로 호출됨.
이름영어설명
eqequal같다
nenot equal다르다
gtgreater than크다
gegreater than or equal크거나 같다
ltless than작다
leless than or equal작거나 같다

클래스 변수와 메소드

  • 인스턴스가 속성과 기능을 가지듯 클래스도 속성(변수)과 기능(함수)을 가질 수 있음.
  • 클래스 변수
    • 클래스 변수 만들기
      • class 클래스 이름:
        	클래스 변수 =
    • 클래스 변수에 접근하기
      • 클래스 이름.변수이름
      • 클래스 내부와 외부에서 접근할 때 위와 같이 작성
# 클래스 선언
class Student:
	count = 0
    
    def __init__(self, name, korean, english, math):
    	# 인스턴스 변수 초기화
        self.name = name
        self.korean = korean
        self.english = english
        self.math = math
        
        # 클래스 변수 설정
        Student.count += 1
        print(f"{Student.count}번째 학생이 생성되었습니다.")
        
# 학생 리스트 선언
students = [
	Student("윤인성", 87, 88, 98),
    Student("연하진", 98, 96, 98),
    Student("구지연", 87, 88, 79),
    Student("나선주", 99, 87, 88)
]

# 출력
print()
print(f"현재 생성된 총 학생 수는 {Student.count}명입니다.")

결과값

1번째 학생이 생성되었습니다.
2번째 학생이 생성되었습니다.
3번째 학생이 생성되었습니다.
4번째 학생이 생성되었습니다.

현재 생성된 총 학생 수는 4명입니다.
  • 클래스 함수
    • 클래스를 가진 함수
    • 클래스가 가진 기능이라고 명시적으로 나타내는 것 뿐!
    • 클래스 함수 만들기(데코레이터 사용함)
      • class 클래스 이름:
        	@classmethod
            def 클래스 함수(cls, 매개변수):
            	pass
      • 클래스 함수의 첫 번쨰 매개변수 cls는 클래스 자체
    • 클래스 함수 호출하기
      • 클래스 이름.함수 이름(매개변수)
# 클래스 선언
class Student:
	# 클래스 변수
	count = 0
    students = []
    
    # 클래스 함수
    @classmethod
    def print(cls):
    	print("----- 학생 목록 -----")
        print("이름\t총점\t평균")
        for student in cls.students:
        	print(str(student))
        print("----- ----- -----")
        
    # 인스턴스 함수    
    def __init__(self, name, korean, english, math):
    	# 인스턴스 변수 초기화
        self.name = name
        self.korean = korean
        self.english = english
        self.math = math
        
        # 클래스 변수
        Student.count += 1
        Student.students.append(self)
        
    def get_sum(self):
    	return self.korean + self.english + self.math
        
    def get_average(self):
    	return self.get_sum() / 3
        
    def __str__(self):
    	return f"{self.name}\t{self.get_sum()}\t{self.get_average()}"
        
# 학생 리스트 선언
Student("윤인성", 87, 88, 98)
Student("연하진", 98, 96, 98)
Student("구지연", 87, 88, 79)
Student("나선주", 99, 87, 88)

# 현재 생성된 학생을 모두 출력
Student.print()

결과값

----- 학생 목록 -----
이름	총점	평균
윤인성	273	91.0
연하진	292	97.33333333333333
구지연	254	84.66666666666667
나선주	274	91.33333333333333
----- ----- -----

3. Getter와 Setter

프라이빗 변수

  • OOP의 측징 중 하나인 캡슐화를 위해서는 프라이빗 변수가 필요!
  • 클래스 내부의 변수를 외부에서 사용하는 것을 막기 위해 사용
  • __<변수 이름>형태로 선언
  • 이때 _(언더바)가 두 개라는 것을 주의!
# 모듈 가져오기
import math

# 클래스 선언
class Circle:
    def __init__(self, radius):
        self.__radius = radius

    def get_circumference(self):
        return 2 * math.pi * self.__radius

    def get_area(self):
        return math.pi * (self.__radius ** 2)  # **연산자는 제곱을 뜻함.


# 원의 둘레와 넓이 구하기
circle = Circle(10)
print("#원의 둘레와 넓이를 구하기")
print("원의 둘레 :", circle.get_circumference())
print("원의 넓이 :", circle.get_area())
print()

# __radius에 접근
print("# __radius에 접근")
print(circle.__radius)
#원의 둘레와 넓이를 구하기
원의 둘레 : 62.83185307179586
원의 넓이 : 314.1592653589793

# __radius에 접근
Traceback (most recent call last):
  File "C:/Users/21528463/PycharmProjects/pythonProject1/private_var.py", line 25, in <module>
    print(circle.__radius)
AttributeError: 'Circle' object has no attribute '__radius'

게터와 세터

  • 프라이빗 변수의 값을 추출하거나 변경하기 위해서 사용되는 것
  • 간접적으로 속성에 접근하도록 해주는 함수
  • 두가지 방식이 있음
    • 데코레이터 사용하지 않은 방식
# 클래스 선언
class Circle:
    ...생략

    # 게터와 세터
    def get_radius(self):
        return self.__radius

    def set_radius(self, value):
        self.__radius = value


# 원의 둘레와 넓이 구하기
circle = Circle(10)
print("#원의 둘레와 넓이를 구하기")
print("원의 둘레 :", circle.get_circumference())
print("원의 넓이 :", circle.get_area())
print()

# 간접적으로 __radius에 접근
print("# __radius에 접근")
print(circle.get_radius())
print()

# __radius 값 변경 후 원의 둘레와 넓이 구하기
circle.set_radius(2)
print("#원의 둘레와 넓이를 구하기")
print("원의 둘레 :", circle.get_circumference())
print("원의 넓이 :", circle.get_area())
print()
#원의 둘레와 넓이를 구하기
원의 둘레 : 62.83185307179586
원의 넓이 : 314.1592653589793

# __radius에 접근
10

#원의 둘레와 넓이를 구하기
원의 둘레 : 12.566370614359172
원의 넓이 : 12.566370614359172

  • 데코레이터 사용 방식
# 클래스 선언
class Circle:
    ...생략

    # 게터와 세터
    @property
    def radius(self):
        return self.__radius
    @radius.setter
    def radius(self, value):
        self.__radius = value


# 원의 둘레와 넓이 구하기
circle = Circle(10)
print("#원의 둘레와 넓이를 구하기")
print("원의 둘레 :", circle.get_circumference())
print("원의 넓이 :", circle.get_area())
print()

# 간접적으로 __radius에 접근
print("# __radius에 접근")
print(circle.radius)
print()

# __radius 값 변경 후 원의 둘레와 넓이 구하기
circle.radius = 2
print("#원의 둘레와 넓이를 구하기")
print("원의 둘레 :", circle.get_circumference())
print("원의 넓이 :", circle.get_area())
print()

결과값

#원의 둘레와 넓이를 구하기
원의 둘레 : 62.83185307179586
원의 넓이 : 314.1592653589793

# __radius에 접근
10

#원의 둘레와 넓이를 구하기
원의 둘레 : 12.566370614359172
원의 넓이 : 12.566370614359172

4. 상속

상속(inheritance) : 기존 class를 그대로 물려받아 업그레이드시켜 사용하는 방법

# 부모 클래스 선언
# 부모 클래스 선언
class Parent:
    def __init__(self):
        self.value = "테스트"
        print("Parent 클래스의 __init__() 메소드가 호출되었습니다.")
    def test(self):
        print("Parent 클래스의 test() 메소드입니다.")

# 자식 클래스 선언
class Child(Parent):
    def __init__(self):
        Parent.__init__(self)
        print("Child 클래스의 __init__() 메소드가 호출되었습니다.")


# 자식 클래스의 인스턴스를 생성하고 부모의 메소드를 호출
child = Child()
child.test()
print(child.value)

결과값

Parent 클래스의 __init__() 메소드가 호출되었습니다.
Child 클래스의 __init__() 메소드가 호출되었습니다.
Parent 클래스의 test() 메소드입니다.
테스트

다중상속 : 다른 누군가가 만들어 놓은 형태들를 조립해서 내가 원하는 것을 만드는 것

  • 다중 상속은 거의 사용되지 않는 고급기술

출처

참고자료

profile
어떠한 가치를 창출할 수 있을까를 고민하는 개발자. 주로 Spring으로 개발해요.

0개의 댓글