profile image

L o a d i n g . . .

unit 34. 클래스 사용하기

 

  • 클래스와 메서드 만들기

특정한 개념이나 모양으로 존재하는 것을 객체(object)라고 부른다. 그리고 프로그래밍으로 객체를 만들 때 사용하는 것이 클래스이다.

 

게임의 기사 캐릭터를 예로 들어 보겠다.

캐릭터를 클래스로 표현할 때는 체력, 마나, 물리 공격력, 주문력 등의 데이터가 필요한데 이를 클래스의 속성(attribute)이라 부르고, 베기, 찌르기 등의 기능을 메서드(method)라고 부른다.

 

그리고 이렇게 클래스와 객체를 이용한 프로그래밍 방법을 객체지향(object oriented) 프로그래밍이라고 한다.

객체지향 프로그래밍은  현실 세계의 복잡한 문제를 처리하는데 유용하며 기능을 개선하고 발전시킬 때도 해당 클래스만 수정하면 되므로 유지 보수에도 효율적이다.

 

 

* 클래스와 메서드 만들기

클래스는 class에 클래스 이름을 지정하고 :(콜론)을 붙인 뒤 다음 줄부터 def로 메서드를 작성하면 된다.

여기서 메서드는 클래스 안에 들어있는 함수를 뜻한다.

 

보통 파이썬에서는 클래스의 이름은 대문자로 시작한다.

그리고 메서드 작성 방법은 함수와 같다.

또한 메서드의 첫 번째 매개변수는 반드시 self를 지정해야 합니다.

class 클래스이름:
    def 메서드(self):
        코드

다음은 간단한 사람 클래스이다.

>>> class Person:
...     def greeting(self):
...         print('Hello')
...

그런데 클래스는 특정 개념을 표현만 할뿐 사용을 하려면 인스턴스를 생성해야 한다.

만든 클래스를 사용하려면, 클래스에 ()(괄호)를 붙인 뒤 변수에 할당해야한다.

- 인스턴스 = 클래스()

>>> james = Person()

Person으로 변수 james를 만들었는데 이 james Person의 인스턴스(instance)이다.

 

 

* 메서드 호출하기

메서드는 클래스가 아니라 인스턴스를 통해 호출한다.

다음과 같이 인스턴스 뒤에 .(점)을 붙이고 메서드를 호출하면 된다.

- 인스턴스.메서드()

>>> james.greeting()
Hello

 

 

* 인스터스와 객체의 차이점

인스턴스와 객체는 같은 것을 뜻한다.

보통 객체만 지칭할 때는 그냥 객체(object)라고 부르지만, 클래스와 연관지어서 말할 때는 인스턴스(instance)라고 부른다.

 

 

* 메서드 안에서 메서드 호출하기

메서드 안에서 메서드를 호출할 때는 다음과 같이 self.메서드() 형식으로 호출해야 한다.

self 없이 메서드 이름만 사용하면 클래스 바깥쪽에 있는 함수를 호출한다는 뜻이 되므로 주의해야 한다.

class Person:
    def greeting(self):
        print('Hello')
 
    def hello(self):
        self.greeting()    # self.메서드() 형식으로 클래스 안의 메서드를 호출
 
james = Person()
james.hello()    # Hello

 

 

  • 속성 사용하기

더보기

* 속성(attribue)

속성(attribute)을 만들 때는 __init__ 메서드 안에서 self.속성에 값을 할당한다.

class 클래스이름:
    def __init__(self):
        self.속성 = 값

__init__ 메서드는 클래스에 ( )(괄호)를 붙여서 인스턴스를 만들 때 호출되는 특별한 메서드다. 즉, __init__(initialize)이라는 이름 그대로 인스턴스(객체)를 초기화한다.

특히 이렇게 앞 뒤로 __(밑줄 두 개)가 붙은 메서드는 파이썬이 자동으로 호출해주는 메서드인데 스페셜 메서드(special method) 또는 매직 메서드(magic method)라고 부른다.

 

※ 중요 : 속성은 __init__ 메서드에서 만든다는 점과 self에 .(점)을 붙인 뒤 값을 할당한다는 점이 중요함.

 

 

* self의 의미

self는 인스턴스 자기 자신을 의미한다.

따라서 인스턴스를 생성한 후 메서드를 호출하면 현재 인스턴스가 자동으로 매개변수 self에 들어온다.

 

 

* 인스턴스를 만들 때 값 받기

다음과 같이 __init__ 메서드에서 self 다음에 값을 받을 매개변수를 지정한다.

그리고 매개변수를 self.속성에 넣어주면 인스턴스를 만들 때 값을 받을 수 있다.

class 클래스이름:
    def __init__(self, 매개변수1, 매개변수2):
        self.속성1 = 매개변수1
        self.속성2 = 매개변수2

그 후 인스터스를 생성할 때 클래스 뒤 괄호 안에 매개 변수 값들을 콤마로 구분해서 넣어주면 된다. 그러면 괄호 안에 넣은 값은 __init__ 메서드에서 self 뒤에 있는 매개변수에 차례대로 들어가게 된다.

 

+ 클래스 안에서 속성에 접근할 때는 self.속성 형식이었지만, 클래스 바깥에서 속성에 접근할 때는 인스턴스.속성 형식으로 접근한다.

 

  • 비공개 속성 사용하기

더보기

앞에서 배운 속성들은 메서드에서 self로 접근할 수 있고, 인스턴스.속성 형식으로 클래스 바깥에서도 접근할 수 있었다.

 

그런데 클래스 바깥에서는 접근할 수 없고 클래스 안에서만 사용할 수 있는 비공개 속성(private attribute) 또한 존재한다.

비공개 속성은 __속성과 같이 이름이 __(밑줄 두 개)로 시작해야 한다.

단, __속성__처럼 밑줄 두 개가 양 옆에 왔을 때는 비공개 속성이 아니므로 주의해야 한다.

class 클래스이름:
    def __init__(self, 매개변수)
        self.__속성 = 값

비공개 속성은 주로 클래스 바깥으로 드러내고 싶지 않은 값에 사용한다.

즉, 중요한 값인데 바깥에서 함부로 바꾸면 안될 때 비공개 속성을 주로 사용한다. (비공개 속성을 바꾸는 경우는 클래스의 메서드로 한정)

 

공개 속성(public attribute) : 클래스 바깥에서 접근할 수 있는 속성

비공개 속성(private attribute) : 클래스 안에서만 접근할 수 있는 속성

 

 

 

unit 35. 클래스 속성과 정적, 클래스 메서드 사용하기

 

  • 클래스 속성과 인스턴스 속성 알아보기

더보기

속성에는 클래스 속성과 인스턴스 속성 두 가지 종류가 있다. 앞에서 배운 __init__ 메서드에서 만들었던 속성은 인스턴스 속성이다.

클래스 속성은 다음과 같이 클래스에 바로 속성을 만든다.

class 클래스이름:
    속성 = 값

 

사람 클래스에 클래스 속성으로 가방 속성을 넣고 사용해보겠다.

다음과 같이 Person 클래스에 바로 bag 속성을 넣고, put_bag 메서드를 만든다.

그리고 인스턴스 두 개를 만든 뒤 각각 put_bag 메서드를 사용한다.

class Person:
    bag = []
 
    def put_bag(self, stuff):
        Person.bag.append(stuff)
 
james = Person()
james.put_bag('책')
 
maria = Person()
maria.put_bag('열쇠')
 
print(james.bag)
print(maria.bag)

실행 결과는 다음과 같다.

['책', '열쇠']
['책', '열쇠']

james maria 인스턴스를 만들고 각자 put_bag 메서드로 물건을 넣었는데, 

james.bag maria.bag을 출력해보면 넣었던 물건이 합쳐져서 나온다.

이를 통해 클래스 속성은 클래스에 속해 있으며 모든 인스턴스에서 공유함을 알 수 있다.

 

가방을 여러 사람이 공유하지 않으려면 그냥 bag을 인스턴스 속성으로 만들면 된다.

class Person:
    def __init__(self):
        self.bag = []
 
    def put_bag(self, stuff):
        self.bag.append(stuff)
 
james = Person()
james.put_bag('책')
 
maria = Person()
maria.put_bag('열쇠')
 
print(james.bag)
print(maria.bag)

실행 결과는 다음과 같다.

['책']
['열쇠']

 

james.bag maria.bag을 출력해보면 각자 넣은 물건만 출력된다.

즉, 인스턴스 속성은 인스턴스별로 독립되어 있으며 서로 영향을 주지 않음을 알 수 있다.

 

- 클래스 속성: 모든 인스턴스가 공유. 인스턴스 전체가 사용해야 하는 값을 저장할 때 사용

- 인스턴스 속성: 인스턴스별로 독립되어 있음. 각 인스턴스가 값을 따로 저장해야 할 때 사용

 

* 비공개 클래스 속성 사용하기

클래스 속성을 만들 때 __속성과 같이 __(밑줄 두 개)로 시작하면 비공개 속성이 된다.

따라서 클래스 안에서만 접근할 수 있고, 클래스 바깥에서는 접근할 수 없다.

비공개 클래스 속성은 주로 클래스 바깥으로 드러내고 싶지 않은 값에 사용한다.

 

  • 정적 메서드 사용하기

더보기

앞에서는 클래스의 메서드를 사용할 때 인스턴스를 통해서 호출했다.

그런데 정적 메서드라는 인스턴스를 통하지 않고 클래스에서 바로 호출할 수 있는 메서드가 존재한다.

 

정적 메서드는 다음과 같이 메서드 위에 @staticmethod를 붙인다.

이때 정적 메서드는 매개변수에 self를 지정하지 않는다.

class 클래스이름:
    @staticmethod
    def 메서드(매개변수1, 매개변수2):
        코드

 

다음은 간단한 덧셈과 곱셈을 하는 클래스이다.

class Calc:
    @staticmethod
    def add(a, b):
        print(a + b)
 
    @staticmethod
    def mul(a, b):
        print(a * b)
 
Calc.add(10, 20)    # 클래스에서 바로 메서드 호출
Calc.mul(10, 20)    # 클래스에서 바로 메서드 호출

Calc 클래스에서 @staticmethod를 붙여서 add 메서드와 mul 메서드를 만들었다.

그리고 정적 메서드를 호출할 때는 다음과 같이 클래스에서 바로 메서드를 호출하면 된다.

- 클래스.메서드()

 

정적 메서드는 self를 받지 않으므로 인스턴스 속성에는 접근할 수 없다. 그래서 보통 정적 메서드는 인스턴스 속성, 인스턴스 메서드가 필요 없을 때 사용한다.

 

(정적 메서드는 인스턴스의 상태를 변화시키지 않는 메서드를 만들 때 사용)

 

 

* 인스턴스 메서드와 정적 메서드

인스턴스의 내용을 변경해야 할 때는 인스턴스 메서드로 작성하면 되고,

인스턴스 내용과는 상관없이 결과만 구하면 될 때는 정적 메서드로 작성하면 되는 것.

 

  • 클래스 메서드 사용하기

더보기

클래스 메서드정적 메서드와 비슷하지만 약간의 차이점이 있다.

클래스 메서드는 다음과 같이 메서드 위에 @classmethod를 붙인다.

이때 클래스 메서드는 첫 번째 매개변수에 cls를 지정해야 한다.(cls class에서 따옴).

class 클래스이름:
    @classmethod
    def 메서드(cls, 매개변수1, 매개변수2):
        코드

클래스 메서드는 첫 번째 매개변수가 cls인데 여기에는 현재 클래스가 들어온다. 따라서 cls로 클래스 속성에 접근할 수 있다.

 

클래스 메서드는 정적 메서드처럼 인스턴스 없이 호출할 수 있다는 점은 같다.

하지만 클래스 메서드는 메서드 안에서 클래스 속성, 클래스 메서드에 접근해야 할 때 사용한다.

 

 

unit 36. 클래스 상속 사용하기

 

  • 사람 클래스로 학생 클래스 만들기

더보기

클래스 상속은 물려받은 기능을 유지한채로 다른 기능을 추가할 때 사용하는 기능이다.

여기서 기능을 물려주는 클래스를 기반 클래스(base class) 혹은 부모 클래스(parent class), 상속을 받아 새롭게 만드는 클래스를 파생 클래스(derived class) 혹은 자식 클래스(child class)라고 한다.

 

클래스 상속은 다음과 같이 클래스를 만들 때 ( )(괄호)를 붙이고 안에 기반 클래스 이름을 넣는다.

class 기반클래스이름:
    코드
 
class 파생클래스이름(기반클래스이름):
    코드

 

다음은 사람 클래스를 만들고 사람 클래스를 상속받아 학생 클래스를 만든 것이다.

class Person:
    def greeting(self):
        print('안녕하세요.')
 
class Student(Person):
    def study(self):
        print('공부하기')
 
james = Student()
james.greeting()    # 안녕하세요.: 기반 클래스 Person의 메서드 호출
james.study()       # 공부하기: 파생 클래스 Student에 추가한 study 메서드

Student 클래스에는 greeting 메서드가 없지만 Person 클래스를 상속받았으므로 greeting 메서드를 호출할 수 있다. 그리고 Student 클래스에 추가한 새로운 메서드인 study도 호출할 수 있다.

이처럼 클래스 상속은 기반 클래스의 기능을 유지하면서 새로운 기능을 추가할 수 있다. 즉, 기존 기능을 재사용할 수 있어서 효율적이다.

 

  • 상속 관계와 포함 관계 알아보기

더보기

* 상속 관계

상속은 명확하게 같은 종류이며 동등한 관계일 때 사용한다.

예를 들어, 다음과 같이 학생은 사람과 상속 관계이므로 클래스 상속을 사용할 수 있다.

class Person:
    def greeting(self):
        print('안녕하세요.')
 
class Student(Person):
    def study(self):
        print('공부하기')

 

* 포함 관계

하지만 다음의 경우에는 리스트 속성에 Person 인스턴스를 넣어서 관리하면 된다.

class Person:
    def greeting(self):
        print('안녕하세요.')
 
class PersonList:
    def __init__(self):
        self.person_list = []    # 리스트 속성에 Person 인스턴스를 넣어서 관리
 
    def append_person(self, person):    # 리스트 속성에 Person 인스턴스를 추가하는 함수
        self.person_list.append(person)

여기서는 상속을 사용하지 않고 속성에 인스턴스를 넣어서 관리하므로 PersonList Person을 포함하고 있다. 이러면 사람 목록 PersonList와 사람 Person은 동등한 관계가 아니라 포함 관계이다.

 

※ 정리 : 같은 종류에 동등한 관계일 때는 상속을 사용하고, 그 이외에는 속성에 인스턴스를 넣는 포함 방식을 사용하면 된다.

 

  • 기반 클래스의 속성 사용하기

더보기

* super()로 기반 클래스 초기화하기

기반 클래스의 인스턴스 속성을 사용하기 위해서는 super()를 사용해서 기반 클래스의 __init__ 메서드를 호출해줘야한다.

다음과 같이 super() 뒤에 .(점)을 붙여서 메서드를 호출하는 방식이다.

- super().메서드()

super().__init__()와 같이 기반 클래스 Person __init__ 메서드를 호출해주면 기반 클래스가 초기화되어서 속성이 만들어진다.

 

 

* 기반 클래스를 초기화하지 않아도 되는 경우

만약 파생 클래스에서 __init__ 메서드를 생략한다면 기반 클래스의 __init__이 자동으로 호출되므로 super()는 사용하지 않아도 된다.

즉, 파생 클래스에 __init__ 메서드가 없다면 기반 클래스의 __init__이 자동으로 호출되므로 기반 클래스의 속성을 사용할 수 있다.

 

  • 메서드 오버라이딩 사용하기

더보기

메서드 오버라이딩이란? 파생 클래스에서 기반 클래스의 메서드를 새로 정의하는 것을 말한다.

오버라이딩(overriding)은 무시하다, 우선하다라는 뜻을 가지고 있는데 말 그대로 기반 클래스의 메서드를 무시하고 새로운 메서드를 만든다는 뜻이다.

사용방법은 파생 클래스에서 기반 클래스와 같은 이름의 메서드를 정의해서 사용하면 된다.

 

그리고 메서드 오버라이딩 후 다시 기반 클래스의 메서드를 사용하고 싶을 땐 super()로 기반 클래스의 메서드를 호출하면 된다.

 

즉, 중복되는 기능은 파생 클래스에서 다시 만들지 않고, 기반 클래스의 기능을 사용하면 된다.

따라서 메서드 오버라이딩은 원래 기능을 유지하면서 새로운 기능을 덧붙일 때 사용한다.

 

  • 다중 상속 사용하기

더보기

다중 상속은 여러 기반 클래스로부터 상속을 받아서 파생 클래스를 만드는 방법이다.

다음과 같이 클래스를 만들 때 ( )(괄호) 안에 클래스 이름을 ,(콤마)로 구분해서 넣는다.

class 기반클래스이름1:
    코드
 
class 기반클래스이름2:
    코드
 
class 파생클래스이름(기반클래스이름1, 기반클래스이름2):
    코드

다중 상속을 이용하면, 기반 클래스 여러 개의 메소드와 속성을 이용할 수 있게 된다.

 

* 메서드의 탐색 순서 확인하기

클래스에 메서드 mro를 사용해보면 메서드 탐색 순서가 나온다.(클래스.__mro__ 형식도 같은 내용)

- 클래스.mro()

 

만약 상속 관계가 복잡하게 얽혀 있다면 MRO를 살펴보는 것이 편리하다.

(+ 파이썬은 다중 상속을 한다면 클래스 목록 중 왼쪽에서 오른쪽 순서로 메서드를 찾음.)  

 

  • 추상 클래스 사용하기

더보기

추상 클래스는 메서드의 목록만 가진 클래스이며 상속받는 클래스에서 메서드 구현을 강제하기 위해 사용한다.

먼저 추상 클래스를 만들려면 import abc 모듈을 가져와야 한다.( abc abstract base class의 약자임).

그리고 클래스의 ( )(괄호) 안에 metaclass=ABCMeta를 지정하고, 메서드를 만들 때 위에 @abstractmethod를 붙여서 추상 메서드로 지정한다.

from abc import *
 
class 추상클래스이름(metaclass=ABCMeta):
    @abstractmethod
    def 메서드이름(self):
        코드

만약 추상 클래스를 상속받았다면 파생 클래스는 @abstractmethod가 붙은 추상 메서드를 모두 구현해야 한다.

따라서 추상 클래스는 파생 클래스가 반드시 구현해야 하는 메서드를 정해줄 수 있다.

 

* 추상 메서드를 빈 메서드로 만드는 이유

추상 클래스는 인스턴스로 만들 경우 에러가 발생한다. 즉, 추상 클래스는 인스턴스로 만들 수 없다.

그래서 지금까지 추상 메서드를 만들 때 pass만 넣어서 빈 메서드로 만든 것이다. 왜냐하면 추상 클래스는 인스턴스를 만들 수 없으니 추상 메서드도 호출할 일이 없기 때문이다.

 

※ 정리 : 추상 클래스는 인스턴스로 만들 때는 사용하지 않으며 오로지 상속에만 사용한다. 그리고 파생 클래스에서 반드시 구현해야 할 메서드를 정해 줄 때 사용한다.

 

 

unit 37. 두 점 사이의 거리 구하기

 

  • 두 점 사이의 거리 구하기

더보기

* 클래스로 점 구현하기

다음은 Point2D 클래스를 구현해 x y를 속성으로 넣는다.

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

이제 Point2D 클래스로 점 두 개를 만들면 다음과 같다.

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
p1 = Point2D(x=30, y=20)    # 점1
p2 = Point2D(x=60, y=50)    # 점2
 
print('p1: {} {}'.format(p1.x, p1.y))    # 30 20
print('p2: {} {}'.format(p2.x, p2.y))    # 60 50

 

* 피타고라스의 정리로 두 점 사이의 거리 구하기

두 점 사이의 거리는 피타고라스 정리로 구하면 되는데, 피타고라스 정리는 다음과 같다.

 

- 임의의 직각삼각형에서 빗변을 한 변으로 하는 정사각형의 넓이는 다른 두 변을 각각 한 변으로 하는 정사각형의 넓이의 합과 같다.

- a2 + b2 = c2

 

피타고라스의 정리에 대입하려면 먼저 선 a b의 길이를 구해야 한다.

Point2D 클래스의 인스턴스에 두 점의 좌표 정보가 들어있으므로 인스턴스를 활용하면 된다.

a = p2.x - p1.x    # 선 a의 길이
b = p2.y - p1.y    # 선 b의 길이

a p2 x에서 p1 x를 빼면 되고, b p2 y에서 p1 y를 빼면 된다.

그다음에 피타고라스의 정리에서 c의 길이를 계산하려면 제곱근을 구해야 한다.

이때는 math 모듈의 sqrt 함수를 사용하면 된다.

- math.sqrt(값)

 

이제 sqrt 함수까지 사용해서 p1 p2의 거리를 구해보면 다음과 같다.

import math
 
class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y
 
p1 = Point2D(x=30, y=20)    # 점1
p2 = Point2D(x=60, y=50)    # 점2
 
a = p2.x - p1.x    # 선 a의 길이
b = p2.y - p1.y    # 선 b의 길이
 
c = math.sqrt((a * a) + (b * b))    # (a * a) + (b * b)의 제곱근을 구함
print(c)    # 42.42640687119285
복사했습니다!