Python 클래스 기본개념 1-3

2022-03-12

.

Python_studynote(20220312)

[학습자료]

패스트 캠퍼스 “초격차 패키지 : 한 번에 끝내는 파이썬 웹 개발” 강의를 공부하고 정리한 내용입니다.

** URL : https://fastcampus.co.kr/dev_online_pyweb

[관련페이지]

(1) Python 클래스 기본개념 2-3

https://minman2115.github.io/DS_and_algorithm_09

(2) Python 클래스 기본개념 3-3

https://minman2115.github.io/python_DS12

[학습내용]

  • 클래스란

객체를 만들기 위한 설계도

  • 객체

그 설계도로부터 만들어낸 제품

  • 클래스를 사용하는 이유

예를 들어서 피파온라인 게임을 구현한다고 가정하자.

먼저 선수 정의를 위해서 아래와 같이 코드를 작성하였다.

player1_name = "ronaldo"
player1_speed = 95
player1_team = "manUTD"

print(f"{player1_name} 는 월드클래스 선수다.")

player2_name = "son"
player2_speed = 93
player2_team = "TOT"

print(f"{player2_name} 는 월드클래스 선수다.")

player3_name = "van_dijk"
player3_speed = 90
player3_team = "Liverpool"

print(f"{player3_name} 는 월드클래스 선수다.")

def get_player_team(name, team):
    print(f"{name} 의 소속팀은 {team} 이다.")

get_player_team(player1_name, player1_team)
get_player_team(player2_name, player2_team)
get_player_team(player3_name, player3_team)
ronaldo 는 월드클래스 선수다.
son 는 월드클래스 선수다.
van_dijk 는 월드클래스 선수다.
ronaldo 의 소속팀은 manUTD 이다.
son 의 소속팀은 TOT 이다.
van_dijk 의 소속팀은 Liverpool 이다.
  • 클래스를 사용한 경우

위의 경우에서 선수의 수가 많아지면 많아질수록 코드의 수도 급격하게 늘어날 것이다.

반면에 클래스를 사용하면 객체를 생성하는 것보다 코드수를 줄일 수 있고, 효율적으로 관리할 수 있다.

class football_player:
    def __init__(self, name, speed, team):
        self.name = name
        self.speed = speed
        self.team = team
        print(f"{self.name} 는 월드클래스 선수다.")

    def get_player_team(self):
        print(f"{self.name} 의 소속팀은 {self.team} 이다.") 

ronaldo = football_player("호날두",95,"ManUTD")
son = football_player("손흥민",93,"TOT")
van_dijk = football_player("반다이크",90,"Liverpool")

ronaldo.get_player_team()
son.get_player_team()
van_dijk.get_player_team()
호날두 는 월드클래스 선수다.
손흥민 는 월드클래스 선수다.
반다이크 는 월드클래스 선수다.
호날두 의 소속팀은 ManUTD 이다.
손흥민 의 소속팀은 TOT 이다.
반다이크 의 소속팀은 Liverpool 이다.
  • 클래스의 구성

클래스는 크게 속성과 메서드의 집합이다.

(1) 속성

이름, 스피드, 소속팀 등등

(2) 메서드

스프린트하기(스피드 증가시키기), 슈팅하기, 헤딩하기, 패스하기, 질주하기 등등

  • 축구선수 클래스에 속성을 추가해보기

아래와 같이 이름, 스피드, 소속팀 이라는 속성을 init 함수로 정의할 수 있다.

init 메서드는 인스턴스를 만들때 반드시 호출되는 메서드이다.

가장 먼저 호출되기 때문에 init이라고 사용되고 있다.

class football_player:
    def __init__(self, name, speed, team):
        self.name = name
        self.speed = speed
        self.team = team
        print(f"{self.name} 는 월드클래스 선수다.")

# 그러면 언제 인스턴스가 생성이 되냐
# 아래와 같이 football_player 클래스를 호출하는데 
# 속성값을 넣게 되면 init의 매개변수로 들어가게 된다.
# self는 매개변수로 치지 않는다.
# 그리고 아래와 같이 적절하게 변수를 지정하면 클래스 인스턴스를 선언할 수 있다. 
ronaldo = football_player("호날두",95,"ManUTD")
son = football_player("손흥민",93,"TOT")
van_dijk = football_player("반다이크",90,"Liverpool")

위에서 self는 인스턴스 자기 자신을 뜻한다.

자기자신이 무슨말이냐 예를 들어서

ronaldo = football_player("호날두",95,"ManUTD")

요부분에서 스피드 95짜리 호날두 인스턴스를 만들게 되면 여기서 self는 ronaldo 인스턴스 자기자신을 말한다.

그래서 ronaldo라는 인스턴스의 self.name는 호날두가 되는 것이고, self.speed는 95가 되는 것이다.

만약에 아래와 같이 손흥민 인스턴스라면

son = football_player("손흥민",93,"TOT")

self는 손흥민 인스턴스 자기자신을 말하는 것이다.

그래서 son이라는 인스턴스의 self.name는 손흥민이 되는 것이고, self.speed는 93이 되는 것이다.

결론적으로 클래스의 속성을 추가하는 것은 init 함수에다가 인자로 속성값을 넘겨주는 방식으로 추가해주는 방식으로 이루어진다.

init은 생성자라고도 불린다. 인스턴스를 생성할때 호출되는 메서드를 생성자라고 하는데 init 함수가 그런경우이기 때문이다.

  • 클래스에 메서드 구현하기

스피드를 증가시키고, 스피드 상태를 가져오는 메서드를 아래와 같이 구현할 수 있다.

class football_player:
    def __init__(self, name, speed, team):
        self.name = name
        self.speed = speed
        self.team = team
        print(f"{self.name} 는 월드클래스 선수다.")

    def sprint(self,num):
        self.speed += num

    def get_speed(self):
        return self.speed

# van_dijk 인스턴스 생성
van_dijk = football_player("반다이크",90,"Liverpool")
van_dijk.sprint(4)
print(van_dijk.get_speed())

# son 인스턴스 생성
son = football_player("손흥민",93,"TOT")
son.sprint(5)
print(son.get_speed())
반다이크 는 월드클래스 선수다.
94
손흥민 는 월드클래스 선수다.
98
  • 상속 기본개념

상속을 이해하려면 부모클래스와 자식클래스가 있다는 것을 먼저 가정해야한다.

자식클래스가 부모클래스의 속성과 메서드를 그대로 가져올 수 있다는 개념이 상속이다.

만약에 축구선수의 종류가 필드플레이어와 골키퍼가 있다고 가정하자.

수천 수만명의 축구선수를 구현하려고 한다면 클래스로 만든다고 하면 관리하기가 어려울 것이다.

코드를 모두 클래스 하나에 구현을 한다고 하면 유지보수에 한계가 있기 때문이다.

그래서 축구선수라는 공통된 속성과 메소드는 따로 구현해두고 필드플레이어는 필드플레이어 클래스의 속성과 메서드, 골키퍼는 골키퍼 클래스의 속성과 메서드를 분리해서 구현한다면 관리하기가 훨씬 용이해질 것이다. 필드플레이어 클래스를 구현할때 코드를 다 구현할 필요가 없이 축구선수의 클래스를 상속해서 가져오기만 하면된다. 골키퍼 클래스도 마찬가지다.

  • 상속 구현해보기

(1) 부모클래스

class football_player:
    def __init__(self, name, speed, team):
        self.name = name
        self.speed = speed
        self.team = team
        print(f"{self.name} 는 축구선수다.")

    def save(self):
        print(f"{self.name} 육탄방어로 선방!")

# son 인스턴스 생성
son = football_player("손흥민",93,"TOT")
son.save()

# van_dijk 인스턴스 생성
van_dijk = football_player("반다이크",90,"Liverpool")
van_dijk.save()

# kim 인스턴스 생성
kim = football_player("김병지",40,"Pohang")
kim.save()
손흥민 는 축구선수다.
손흥민 육탄방어로 선방!
반다이크 는 축구선수다.
반다이크 육탄방어로 선방!
김병지 는 축구선수다.
김병지 육탄방어로 선방!

(2) 자식클래스

class attacker(football_player):
    pass

# 메서드 오버라이딩으로 수비수의 선방에 대한 메서드를 구현
class defender(football_player):
    def save(self):
        print(f"{self.name} 수비수다운 기술적인 태클로 선방!")

# 메서드 오버라이딩으로 골키퍼의 선방에 대한 메서드를 구현
class goalkeeper(football_player):
    def save(self):
        print(f"{self.name} 손끝으로 선방!")

# son 인스턴스 생성
son = attacker("손흥민",93,"TOT")
son.save()

# van_dijk 인스턴스 생성
van_dijk = defender("반다이크",90,"Liverpool")
van_dijk.save()

# kim 인스턴스 생성
kim = goalkeeper("김병지",40,"Pohang")
kim.save()
손흥민 는 축구선수다.
손흥민 육탄방어로 선방!
반다이크 는 축구선수다.
반다이크 수비수다운 기술적인 태클로 선방!
김병지 는 축구선수다.
김병지 손끝으로 선방!
  • 생성자 오버라이딩 구현예시

생성자 오버라이딩으로 골키퍼의 스킬을 구현해보자.

import random

class football_player:
    def __init__(self, name, speed, team):
        self.name = name
        self.speed = speed
        self.team = team
        print(f"{self.name} 는 축구선수다.")

    def save(self):
        print(f"{self.name} 육탄방어로 선방!")

class attacker(football_player):
    pass

class defender(football_player):
    def save(self):
        print(f"{self.name} 수비수다운 기술적인 태클로 선방!")

class goalkeeper(football_player):
    def __init__(self, name, speed, team, skills):
        # super()라는 메서드를 통해 부모의 속성값을 불러올 수 있다.
        # 아래의 경우는 football_player의 속성을 불러오게 된다.
        super().__init__(name, speed, team)
        self.skills = skills

    def save(self):
        print(f"{self.name} 손끝으로 선방!")

    def skill(self):
        # skills 중에 임의로 하나의 스킬을 사용하는 메서드
        print(f"{self.name}{self.skills[random.randint(0,2)]}")

# son 인스턴스 생성
son = attacker("손흥민",93,"TOT")
son.save()

# van_dijk 인스턴스 생성
van_dijk = defender("반다이크",90,"Liverpool")
van_dijk.save()

# kim 인스턴스 생성
kim = goalkeeper("김병지",40,"Pohang",("키퍼 쓰로잉패스","수비수 지휘","정교한 키퍼 롱패스"))
kim.save()

위와 같이 스킬들을 인자값들로 받을 수 있고 아니면 아래오 같이 속성값에 고정해서 사용할 수도 있다.

import random

class football_player:
    def __init__(self, name, speed, team):
        self.name = name
        self.speed = speed
        self.team = team
        print(f"{self.name} 는 축구선수다.")

    def save(self):
        print(f"{self.name} 육탄방어로 선방!")

class attacker(football_player):
    pass

class defender(football_player):
    def save(self):
        print(f"{self.name} 수비수다운 기술적인 태클로 선방!")

class goalkeeper(football_player):
    def __init__(self, name, speed, team):
        # super()라는 메서드를 통해 부모의 속성값을 불러올 수 있다.
        # 아래의 경우는 football_player의 속성을 불러오게 된다.
        super().__init__(name, speed, team)
        self.skills = ("키퍼 쓰로잉패스","수비수 지휘","정교한 키퍼 롱패스")

    def save(self):
        print(f"{self.name} 손끝으로 선방!")

    def skill(self):
        # skills 중에 임의로 하나의 스킬을 사용하는 메서드
        print(f"{self.name}{self.skills[random.randint(0,2)]}")

# son 인스턴스 생성
son = attacker("손흥민",93,"TOT")
son.save()

# van_dijk 인스턴스 생성
van_dijk = defender("반다이크",90,"Liverpool")
van_dijk.save()

# kim 인스턴스 생성
kim = goalkeeper("김병지",40,"Pohang")
kim.save()
kim.skill()
손흥민 는 축구선수다.
손흥민 육탄방어로 선방!
반다이크 는 축구선수다.
반다이크 수비수다운 기술적인 태클로 선방!
김병지 는 축구선수다.
김병지 손끝으로 선방!
김병지 의 정교한 키퍼 롱패스
  • 클래스 변수 기본개념

클래스 변수란 인스턴스들이 모두 공유하는 변수를 말한다.

클래스 변수는 아래와 같이 구현할 수 있다.

예를 들어서 플레이어를 계속 생성할 경우 서버에 부하가 생길 것을 우려하여 축구선수 생성수를 1000명으로 제한하기로 했다.

이런 경우에는 아래와 같이 player_max_num이라는 클래스 변수를 선언하여 이를 구현할 수 있다.

import random

class football_player:

    player_max_num = 1000

    def __init__(self, name, speed, team):
        self.name = name
        self.speed = speed
        self.team = team
        # 클래스 변수는 self를 사용하지 않는다.
        football_player.player_max_num -= 1
        print(f"{self.name} 는 축구선수다.")

    def save(self):
        print(f"{self.name} 육탄방어로 선방!")

class attacker(football_player):
    pass

class defender(football_player):
    def save(self):
        print(f"{self.name} 수비수다운 기술적인 태클로 선방!")

class goalkeeper(football_player):
    def __init__(self, name, speed, team):
        # super()라는 메서드를 통해 부모의 속성값을 불러올 수 있다.
        # 아래의 경우는 football_player의 속성을 불러오게 된다.
        super().__init__(name, speed, team)
        self.skills = ("키퍼 쓰로잉패스","수비수 지휘","정교한 키퍼 롱패스")

    def save(self):
        print(f"{self.name} 손끝으로 선방!")

    def skill(self):
        # skills 중에 임의로 하나의 스킬을 사용하는 메서드
        print(f"{self.name}{self.skills[random.randint(0,2)]}")

# son 인스턴스 생성
son = attacker("손흥민",93,"TOT")
son.save()
print(son.player_max_num)

# van_dijk 인스턴스 생성
van_dijk = defender("반다이크",90,"Liverpool")
van_dijk.save()
print(van_dijk.player_max_num)

# kim 인스턴스 생성
kim = goalkeeper("김병지",40,"Pohang")
kim.save()
kim.skill()
print(kim.player_max_num)
손흥민 는 축구선수다.
손흥민 육탄방어로 선방!
999
반다이크 는 축구선수다.
반다이크 수비수다운 기술적인 태클로 선방!
998
김병지 는 축구선수다.
김병지 손끝으로 선방!
김병지 의 키퍼 쓰로잉패스
997