[Python] class

chanyeong kim·2022년 1월 10일
1

Python

목록 보기
1/2

클래스(class)와 객체(object)

직관적으로 클래스를 이해하는데 보통 붕어빵틀과 붕어빵을 예시로 든다.

  • 붕어빵 틀: 클래스(class)
  • 붕어빵 틀에 의해서 만들어진 붕어빵: 객체(object) 또는 인스턴스(instance)
    • 클래스로 만든 객체를 인스턴스라고도 하는데, 인스턴스라는 말은 특정 객체가 어떤 클래스의 객체인지를 관계 위주로 설명할 때 사용한다.
    • 붕어빵은 객체 (o), 붕어빵은 인스턴스 (x)
    • 붕어빵은 붕어빵 틀의 객체 (x), 붕어빵은 붕어빵 틀의 인스턴스 (o)
  • 클래스는 똑같은 무엇인가를 계속해서 만들어낼 수 있는 설계 도면(붕어빵 틀)이고 객체는 클래스로 만든 피조물을 뜻하며 객체마다 고유의 성격을 가진다.
  • 우리가 붕어빵 틀로 같은 붕어빵을 만들더라도 그 안의 내용물에 따라 팥 붕어빵과 슈크림 붕어빵이 있고 서로 전혀 영향을 주지 않는다.

가장 간단한 예시로,

class frame:
    pass
    
adzuki = frame()
cream = frame()
  • frame() 이라는 클래스의 결과값을 받은 adzuki와 cream이 바로 객체이다.

클래스의 기본 틀

기본적인 클래스의 구조는 다음과 같다.

class 클래스명()
   [변수 1]   # 클래스 변수
   [변수 2]
   
   def 함수1(self, 인자1, 인자2, ......, 인자n)  # 메서드
       <코드 블록>
       ...
      
   def 함수2(self, 인자1, 인자2, ......, 인자n) 
       <코드 블록>
       ...

예시로 만들어 보면,

class Bicycle():
   
   def move(self, speed): #self는 자기 인스턴스를 인식하는 것임, my_bicycle을 인식하는 것
       print("자전거: 시속 {0}킬로미터 전진".format(speed))
   
   def turn(self, direction): 
       print("자전거: {0}회전".format(direction))
       
   def stop(self):
       print("자전거 {0}, {1}: 정지 ".format(self.wheel_size, self.color))


my_bicycle = Bicycle()         # my_bicycle라는 Bicycle의 인스턴스 생성
my_bicycle.wheel_size = 26     # 인스턴스의 속성을 생성하고 값을 넣음
my_bicycle.color = "black"
my_bicycle.move(30)
my_bicycle.turn('좌')
my_bicycle.stop()
자전거: 시속 30킬로미터 전진
자전거: 좌회전
자전거(26, black): 정지

메서드와 매개변수

  • 먼저 클래스 안에 구현되는 함수(move, turn, stop)는 다른 말로 메서드라고 한다. 메서드도 클래스에 포함되어 있다는 점만 제외하면 일반 함수와 다를 것이 없다.
  • 객체를 통해 클래스의 메서드를 호출하려면 도트(.) 연산자를 사용해야 한다.
    • my_bicycle.move(30)
  • 하지만 일반 함수와 달리 메서드의 첫 번째 매개변수 self는 특별한 의미를 가진다. move 메서드에는 self, speed 총 2개의 매개변수가 필요한데 실제로는 1개의 값만 전달했다.
  • 그 이유는 my_bicycle.move(30)처럼 호출하면 move 메서드의 첫 번째 매개변수 self에는 move 메서드를 호출한 객체 my_bicycle이 자동으로 전달되기 때문이다.

  • 클래스 메서드의 첫 번째 매개변수 이름은 관례적으로 self를 사용한다. 객체를 호출할 때 호출한 객체 자신이 전달되기 때문에 self를 사용하는 것이고 다른 이름을 사용해도 상관없다.
def stop(self, wheel_size, color):
    self.wheel_size = wheel_size
    self.color = color
    print("자전거 {0}, {1}: 정지 ".format(self.wheel_size, self.color))

my_bicycle.stop(26, 'black)
자전거(26, black): 정지
  • my_bicycle.stop(26, 'black)처럼 호출하면 stop 메서드의 매개변수 wheel_size, color에는 각각 26과 'black'이 전달되어 다음과 같이 해석된다.
self.wheel_size = 26
self.color = 'black'
  • self는 전달된 객체 my_bicycle이므로 다음과 같이 해석된다.
my_bicycle.wheel_size = 26
my_bicycle.color = 'black'
  • my_bicycle.wheel_size = 26이 실행되면 my_bicycle 객체에 객체변수 wheel_size가 생성되고 값 26이 저장된다. 마찬가지로 my_bicycle.color = 'black'이 실행되면 my_bicycle 객체에 객체변수 color가 생성되고 'black'이 저장된다.

확인을 해보면,

my_bicycle = Bicycle()
my_bicycle.stop(26,'black')
print(my_bicycle.wheel_size)
print(my_bicycle.color)
자전거 26, black: 정지 
26
black

객체 my_bicycle에 객체변수 wheel_sizecolor가 생성된 것을 알 수 있다.

다른 객체의 객체변수 관계!

my_bicycle = Bicycle()
your_bicycle = Bicycle()

my_bicycle.stop(26,'black')
your_bicycle.stop(30,'red')
  • 위와 같이 각각 객체들이 객체 변수 wheel_size를 생성한다고 했을 때 your_bicycle 객체의 객체 변수 wheel_size에는 30의 값이 저장되는 것을 확인할 수 있다.
  • 그렇다면 my_bicycle객체의 wheel_size가 30으로 변할지 그대로 유지되는지 확인을 해보면 그대로 유지되는 것을 확인할 수 있다.
  • 즉, 클래스로 만든 객체의 객체변수는 다른 객체의 객체변수에 상관없이 독립적인 값을 유지한다.

연산기능

기존 Bicycle 클래스에 count라는 연산 기능을 하는 메서드를 추가했다.

class Bicycle():
    
    def move(self, speed): 
        print("자전거: 시속 {0}킬로미터 전진".format(speed))
    
    def turn(self, direction):
        print("자전거: {0}회전".format(direction))
        
    def stop(self, wheel_size, color):
        self.wheel_size = wheel_size
        self.color = color
        print("자전거 {0}, {1}: 정지 ".format(self.wheel_size, self.color))
        
    def count(self):
        result = self.wheel_size * 2
        return result

my_bicycle = Bicycle()
my_bicycle.stop(26, 'black')
print(my_bicycle.count())

my_bicycle.stop 메서드를 실행하면서 self.wheel_size 가 26으로 이미 설정되었고 count 함수에서 곱하기 2를 해주었기 때문에 52가 출력되는 것을 확인할 수 있다.

생성자 (Constructor)

  • 새로운 객체를 생성해서 count 메서드를 바로 실행을 하면 AttributeError가 뜬다. stop 메서드를 실행 해야지만, 객체에 객체변수 self.wheel_size 생성되기 때문이다. 이렇게 객체에 초깃값을 설정해야 할 필요가 있을 때 기본적으로 생성자를 구현하는 것이 더 안전한 방법이다.
  • 생성자란, 객체가 생성될 때 자동으로 호출되는 메서드를 의미하며 __init__을 사용한다!
  • 생성자가 있는 경우 객체를 생성할때 매개변수에 해당하는 값을 전달해서 객체를 생성해야 한다.
    • my_bicycle = Bicycle(20,'blue')
class Bicycle():
    def __init__(self, wheel_size, color):
        self.wheel_size = wheel_size
        self.color = color
    
    def move(self, speed): 
        print("자전거: 시속 {0}킬로미터 전진".format(speed))
    
    def turn(self, direction):
        print("자전거: {0}회전".format(direction))
        
    def stop(self, wheel_size, color):
        self.wheel_size = wheel_size
        self.color = color
        print("자전거 {0}, {1}: 정지 ".format(self.wheel_size, self.color))
        
    def count(self):
        result = self.wheel_size * 2
        return result

클래스 상속

클래스의 상속은 어떤 클래스를 만들 때 다른 클래스의 기능을 물려받을 수 있게 만드는 것이다. 보통 상속은 기존 클래스를 변경하지 않고 기능을 추가하거나 기존 기능을 변경하려고 할 때 사용한다. 기존 클래스가 라이브러리 형태로 제공되거나 수정이 허용되지 않는 상황이 발생하기 때문에 상속을 사용하는 것이다.

class Auto(Bicycle):   # 이렇게 하면 Auto클래스가 Bicycle 클래스를 상속 했으므로 모든 기능을 사용할 수 있음
    
    def count4(self):
        result = self.wheel_size * 4
        return result

오버라이딩

상속받은 클래스에 메서드를 쓰면서 오류가 생기거나, 혹은 메서드를 상황에 맞게 변경을 해주고 싶다면 자녀 클래스에서 부모 클래스에 있는 메서드를 동일한 이름으로 다시 만드는 것을 메서드 오버라이딩 이라고 한다. 이렇게 메서드를 오버라이딩하면 부모 클래스의 메서드 대신 오버라이딩한 메서드가 호출된다.

class Auto(Bicycle):   # 이렇게 하면 Auto클래스가 Bicycle 클래스를 상속 했으므로 모든 기능을 사용할 수 있음
    
    def count(self):	# 부모 클래스인 Bicycle의 count 메서드를 변경해서 Auto 클래스에 적용!
        result = self.wheel_size ** 2
        return result

클래스 변수

클래스 변수는 클래스 안에 선언하여 생성하고 객체들을 통해서도 클래스 변수를 사용할 수 있다. 만약 클래스 변수를 변경하면, 클래스로 만든 객체들을 통해서도 모두 클래스 값이 변경되는 것을 알 수 있다.

class Bicycle():
    today='월'
    pass
    
print(Bicycle.today)    # 월이 출력된다.

a = Bicycle()
print(a.today)      	# 월이 출력된다.

Bicycle.today = '화'

print(a.today)			# 화가 출력된다.

💡요약

class Car():
    
    instance_count = 0                          # 클래스 변수 생성 
    
    def __init__(self, size, color):
        self.size =  size            # 인스턴스 변수 생성 및 초기화
        self.color = color           # 인스턴스 변수 생성 및 초기화
        
        Car.instance_count = Car.instance_count +1   # 클래스 변수 이용, '클래스명.변수명' 형식으로 접근
        print("자동차 객체의 수:{0}".format(Car.instance_count))
        
    def move(self, speed):           
        self.speed = speed
        print("자동차({0} & {1})가 ".format(self.size, self.color), end='')
        
        print("시속 {0}킬로미터로 전진".format(self.speed))
    
    def auto_cruise(self):
        print("자율 주행 모드")
        self.move(self.speed)        
   
   
    # 정적 메서드, 클래스에 직접 접근할 수 있는 메서드 
    @staticmethod
    def check_type(model_code):       # check_type라는 함수를 선언했고 이건 인스턴스에 종속하는 함수가 아님
        if(model_code >= 20):
            print("이 자동차는 전기차입니다.")               
        elif(10 <= model_code < 20):
            print("이 자동차는 가솔린차입니다.")
        else:
            print("이 자동차는 디젤차입니다.")
            
            
    # 클래스 메서드, 외부의 영향없이 클래스만의 변수들, 함수들을 쓰려고!
    # cls를 쳐주면서 Car라는 클래스의 것들을 맘대로 쓸 수 있다.
    @classmethod
    def count_instance(cls):
        print("자동차 객체의 개수: {0}".format(cls.instance_count)) 



# 클래스 상속
class Truck(Car):
    
    #클래스 오버라이딩
    def auto_cruise(self):
        print("자율 주행 모드 시작하겠습니다.")
        self.move(self.speed) 

0개의 댓글