[Python]

파이썬 - 클래스와 객체

AI gina 2022. 5. 23. 18:25

class란? (여러 개의 기능들을 모아놓은 것)

  • 실세계의 것을 모델링하여 속성(attribute)와 행위(method)를 갖는 데이터 타입이다.
  • python에서의 string, int, list, dict.. 모두가 다 클래스로 존재한다.
  • 예를들어 학생이라는 클래스를 만든다면, 학생을 나타내는 속성과 학생이 행하는 행동을 함께 정의 할 수 있음
  • 사용하고자하는 데이터(변수)와 데이터를 다루는 연산(함수)를 하나로 캡슐화(encapsulation)하여 클래스로 표현 (=> 데이터와 함수를 묶어놓은 것)
  • 추상화 : 필요한 것은 남기고 불필요한 것은 없애는 것.
class Person:
    '''이것은 사람을 추상화 한
    클래스 입니다.'''
    name = 'kang'
    def greet(self):
        print('hello~~')
        
kang = Person()  #Person이라는 클래스를 가지고 kang이라는 인스턴스를 만듬.
kang.greet()
kim = Person()
kim.name = 'kim'
print(kim.name)
hello~~
kim

 

object 란?

  • 클래스로 생성되어구체화된 객체(인스턴스)이다.
  • 파이썬의 모든 것(int, str, list..etc)은 객체(인스턴스)이다.
  • 실제로 class가 인스턴스화 되어 메모리에 상주하는 상태를 의미한다.
  • class가 설계도, object는 실제로 설계도를 기반으로 만든 것을 말한다.

class 선언하기

  • 객체를 생성하기 위해선 객체의 설계도가 되는 class를 미리 선언해야 한다.

 

__init__(self)

  • 생성자, 클래스 인스턴스가 생성될 때 호출된다. (객체가 생성될때 초기화 역할을 하는 것)
  • self인자는 항상 첫번째에 오며 자기 자신을 가리키다.
  • 이름이 꼭 self일 필요는 없지만, 관례적으로 self로 사용한다.
  • 생성자에서는 해당 클래스가 다루는 데이터를 정의한다.
    • 이 데이터를 멤버 변수(member variable) 또는 속성(attribute)라고 함
class Pen:
    def __init__(self) :
        self.color = 'black'
        
    def draw(self):
        print(self.color,"색으로 그려요!")
        
p1 = Pen() 
p1.draw() 
# print(p1.color) 


class Pen:
    def __init__(self,color):  #self라는 예약어를 넣어야 함. (단어는 달라질수 있으나, 관용적으로 self사용)
        self.color = color    #color 초기화. self의 color (인스턴스)
        
    def draw(self):           #method에서는 첫번째 인자를 self(자기자신)을 가르킴
        print(self.color,"색으로 그려요!")
 

p2 = Pen('red')
p2.draw()
p3 = Pen('blue')
p3.draw()

 

class Person:
    def __init__(self,name):        #name 매개변수
        print('person의 생성자 실행')
        self.name = name            #self.name 멤버변수
        self.age = 1
        
kang= Person('kang')         #'kang'이라는 매개변수를 Person 클라스에 넣어서 kang이라는 인스턴스 생성
kim=Person('kim')
print('이름', kang.name)      #kang.name 인스턴스변수
print('나이',kang.age)        #kang.age 인스턴스변수
person의 생성자 실행
person의 생성자 실행
이름 kang
나이 1

 

class Person:
    def __init__(self, name, age=1):
        print('person의 생성자 실행')
        self.name = name     
        self.age = age
        
kk =Person('carami',20)
print('이름',kk.name)
print('나이',kk.age)

mm =Person('mmas')
print('이름',mm.name)
print('나이',mm.age)
person의 생성자 실행
이름 carami
나이 20
person의 생성자 실행
이름 mmas
나이 1

self

  • 파이썬의 method는 항상 첫번째 인자로 self를 전달한다. (클래스 안에 정의된 함수는 Method라고 부름)
  • self는 현재 해당 메쏘드가 호출되는 객체 자신을 가리킨다.
  • 반드시 이름이 self일 필요는 없으나 관례적으로 self를 사용한다.
  • 위치는 항상 맨 첫번째 parameter이다.
# 속성으로 name과 speed를 갖는 Car 클래스를 정의하세요.

class Car:
    def __init__(self):
        self.name = 'None'
        self.speed = 0
        
# 본인이 갖고 싶은 차를 생성해 보세요.

ionic = Car()
ionic.name='ionic'
ionic.speed=100
print(ionic.name, ionic.speed)

ionic 100  #결과값
#---------------------------------------------------------------------------------
# 위의 Car 클래스에 차의 이름과 속도를 알려주는 info라는 이름의 메소드를 정의하세요.

class Car:
    def __init__(self):
        self.name = 'None'
        self.speed = 0
        
    def info(self):
        print(f'내 차의 이름은 {self.name}이고, 현재 속도는 {self.speed}입니다.')
   
ionic = Car()
ionic.name='ionic'
ionic.speed=100

ionic.info() #오브젝트명.메쏘드로 호출

내 차의 이름은 ionic이고, 현재 속도는 100입니다. #결과값

 

mehtod 정의

  • 파이썬에서는 메서드의 첫번째 매개변수 이름은 관례적으로 self를 사용한다.
  • obj.method() 형태로 호출된다.
#Counter 클래스를 정의하세요. 멤버변수는 num을 갖고 있고
#메소드는 num을 1씩 증가시키는 메소드, num을 0으로 초기화 하는 메소드.
#현재 num의 값을 알려주는 메소드를 정의하세요.

class Counter :
    def __init__(self):
        self.num = 0
        
    def increment(self):
        self.num +=1
    
    def reset(self):
        self.num = 0
        
    def currentValue(self):
        print('현재값은:', self.num)
        
c= Counter()
c.currentValue()

c.increment()
c.currentValue()

c.reset()
c.currentValue()
현재값은: 0
현재값은: 1
현재값은: 0

 

비공개속성

class Person:
    def __init__(self):
        self.__name = 'kang'
        self.age = 20
     
    def getName(self):        #이름 알려주는 메소드 제공(getName)  ****속성에 직접 접근하는 것과 메소드를 통해서 제공해주는 것의 차이
        return self.__name
    def setName(self):         #이름을 바꿔주는 메소드 제공(setName)
        self.__name = name
    def sName(self,name):      #다른구현이 필요하다면 구현..
        self.__setName(name) 

k = Person()
# print(k.__name)     # __ : 비공개속성 지정 (외부에서는 해당 객체 접근 안됨)  *비공개 함수 비공개 메서드도 있음. 
print(k.age) 
print(k.getName())
k.sName('carami')
print(k.getName())
20
kang

 

**클래스 속성 VS 인스턴스 속성

클래스 속성은 클래스에 속해 있으며 모든 인스턴스에서 공유.

class Person:
    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)
['책', '열쇠']
['책', '열쇠']
class Person:
    bag = []
 
    def put_bag(self, stuff):
        Person.bag.append(stuff)    # 클래스 이름으로 클래스 속성에 접근

print(Person.bag)

 

method type

  • instance method - 객체로 호출된다.
    • 메서드는 객체가 생성될때 만들어진다.
  • class method(static method) - class로 호출
    • 클래스 메서드의 경우 객체가 생성되기 전에 미리 생성됨으로 객체를 통해 호출하지 않는다.
    • 정적 메서드(static method), 클래스 메서드(static method) 공통점 : 인스턴스를 통하지 않고 클래스에서 바로 호출할 수 있음.
    • 클래스 메서드는 첫번째 인자를 클래스로 받음.
    • static method는 메서드 위에 @staticmethod를 붙임. 이때 static method는 매개변수에 self를 지정하지 않음
    • static method 메서드의 실행이 외부 상태에 영향을 끼치지 않는 순수 함수(pure function)를 만들 때 사용.
    • 순수 함수는 부수 효과(side effect)가 없고 입력 값이 같으면 언제나 같은 출력 값을 반환합니다. 즉, static method는 인스턴스의 상태를 변화시키지 않는 메서드를 만들 때 사용
class Cal:
    def add(self, a,b):
        print(a+b)
cal = Cal()
cal.add(1,2)    #인스턴스.method명() 으로 사용하여 접근
class Cal:
    @staticmethod
    def add(a,b):
        print(a+b)

Cal.add(1,2)      #클래스이름.staticmethod명() 으로 사용하여 접근
class Cal:
    result = 0
    def __init__(self):
        self.name = 'test'
    @classmethod                # @classmethod :인스턴스화하지 않고 사용할수있음. 클래스메서드 첫번째 변수를 클래스를 넘김
    def cls_add(cls,a,b):
        cls.result = a+b
        print(cls.name)
        return cls.result 
    @staticmethod               # @staticmethod : 인스턴스화하지 않고 사용할수있음. 클래스 속성 필요 없을 때.
    def add(a,b):
        return a+b

Cal.add(2,3)        #클래스명.메소드명()
Cal.cls_add(2,3)    #클래스명.메소드명()

 

class Person:
    count = 0    # 클래스 속성
 
    def __init__(self):
        Person.count += 1    # 인스턴스가 만들어질 때
                             # 클래스 속성 count에 1을 더함
 
    @classmethod             #클래스메서드. 클래스 메서드는 첫 번째 매개변수가 cls인데 여기에는 현재 클래스가 들어옴.
    def print_count(cls):   
        print('{0}명 생성되었습니다.'.format(cls.count))    # cls로 클래스 속성에 접근. cls.count처럼 cls로 클래스 속성 count에 접근
 
james = Person()
maria = Person()
 
Person.print_count()    # 2명 생성되었습니다.
2명 생성되었습니다.

 

Class Inheritance (상속)

  • 다른 클래스의 기능을 그대로 물려받을 수 있다.
  • 기존 클래스에 기능 일부를 추가하거나, 변경하여 새로운 클래스를 정의한다. (특별화 구현)
  • 코드를 재사용할 수 있게된다.
  • 상속 받고자 하는 대상인 기존 클래스는 (Parent, Super, Base class 라고 부른다.)
  • 상속 받는 새로운 클래스는(Child, Sub, Derived class 라고 부른다.)
  • 의미적으로 is-a관계를 갖는다 (일반화 되면 상속 가능)
class Parent:
    def __init__(self):
        self.i = 10
        
class Child(Parent): #상속
    pass

c = Child()
print(c.i)
10

 

class Parent:
    def __init__(self):
        print('Parent생성자 실행')
        self.i = 10
        
class Child(Parent): # 상속
    def __init__(self):      #둘다 __init__있으면 자식 우선.
        self.i = 20
        print('Child 생성자 실행')
    def test(self):
        pass
        
k = Child()
print(k.i)
p = Parent()
Child 생성자 실행
20
Parent생성자 실행

 

class Person:
    def __init__(self,name):
        self.name = name
    def eat(self, food):
        print('{}은 {}을 먹습니다.'.format(self.name,food))
class Student(Person):
#     def __init__(self,name):
#         self.name = 'student'
        pass 
#Student 클래스에 __init__메서드가 없다면, 부모 클래스의 __init_메서드를 실행한다.

s = Student('carami')     #부모 클래스의 __init__메서드는 name을 원하므로, Student 객체를 생성할 때 name을
s.eat('밥')
carami은 밥을 먹습니다.
class Student(Person):
    def __init__(self):  #Student 클래스가 __init__ 메서드를 가진 경우는 Person의 __init__메소드는 실행되지 않는다.
        self.name = 'student'

s = Student()     
s.eat('빵')
student은 빵을 먹습니다.

 

method override

  • 부모 클래스의 method를 재정의(override)한다.
  • 하위 클래스(자식 클래스) 의 인스턴스로 호출시, 재정의된 메소드가 호출된다.
class Person:
    def __init__(self,name):
        self.name = name
    def eat(self, food):
        print('{}은 {}을 먹습니다.'.format(self.name,food))
        
class Student(Person):
    def __init__(self,name):
        self.name = name
    def eat(self, food):
        print('{}은 {}을 맛있게 먹습니다.'.format(self.name,food))
carami = Student('carami')
carami.eat('밥')
carami은 밥을 맛있게 먹습니다.

 

super

  • 하위클래스(자식 클래스)에서 부모클래스의 method를 호출할 때 사용한다.
  • (부모가 구현한 것을 자식이 그대로 호출해서 사용)
class Parent:
    def hello(self):
        print('안녕')
        print('나는 여기에 복잡하고')
        print('어려운 것들을 많이 구현하고 있어요~^^')
        
class Child(Parent):
    def hello(self):
        super().hello()       #super().부모클래스메소드()
        print('오늘은 날씨가 좋네요~^^')
    def study(self):
        print('공부를 재밌게 해요~~')
        
kim=Child()
kim.hello()
안녕
나는 여기에 복잡하고
어려운 것들을 많이 구현하고 있어요~^^
오늘은 날씨가 좋네요~^^

 

다중상속

  • class 클래스명A(클래스명1, 클래스명2, ...)
  • 우선순위 : 자기자신(A) > 앞 순서 인자(클래스명1) > 부모클래스
class P1:
    def hello(self):
        print('hello p11')
class P2:
    def hello(self):
        print('hello p22')
class C(P1):
    def hello(self):
        print('hello pcc')
class D(P2, C):
    pass

test = D()
test.hello()
hello p22

 

print(D.mro())
[<class '__main__.D'>, <class '__main__.P2'>, <class '__main__.C'>, <class '__main__.P1'>, <class 'object'>]

 

추상클래스

  • abc 모듈 사용 해야 함.
  • 추상 클래스를 상속받았다면 @abstractmethod가 붙은 추상 메서드를 모두 구현해야 함.
  • -> 약간의 강제성을 구현할 수 있음.
from abc import *
 
class StudentBase(metaclass=ABCMeta):   #추상클래스
    @abstractmethod           
    def study(self):         #추상메서드
        pass
 
    @abstractmethod           
    def go_to_school(self):   #추상메서드
        pass
 
class Student(StudentBase):   #Student는 StudentBase 추상 클래스를 상속받음
    def study(self):          #추상메서드 구현
        print('공부하기')
 
    def go_to_school(self):   #추상메서드 구현
        print('학교가기')
 
james = Student()
james.study()
james.go_to_school()
공부하기
학교가기

 

연습문제
1. 멤버변수 w,h를 갖는 Shape 클래스를 정의하시오
2. Shape 클래스는 도형을 그리는 draw() 라는 추상 메소드를 포함하게 하세요.
3. Shape을 상속받은 Rect, Circle 클래스를 정의하고, draw() 메소드를 적절하게 구현하세요.
4. 이때 Circle는 반지름을 나타내는 멤버변수 r을 추가해주세요.
5. 도형은 면적을 구하는 메서드도 추가해 보세요

from abc import *

class Shape(metaclass=ABCMeta):
    def __init__(self, w, h) :
        self.w = w
        self.h = h
    @abstractmethod           
    def draw(self):
        print('도형의 그려요')
        
class Rect(Shape):
    def __init__(self,w,h):
        super().__init__(w,h)
    def draw(self):
        print('사각형을 그려요')
    def calculate(self):
        return self.w*self.h

class Circle(Shape):
    def __init__(self, w, h, r) :
        super().__init__(w,h)
        self.r = r
    def draw(self):
        print('원을 그려요')
    def calculate(self):
        return (self.r**2)*3.14 

    
c = Circle(3,4,2)
print(c.draw())
print(c.calculate())


rec = Rect(3,4)
print(rec.draw())
print(rec.calculate())
원을 그려요
None
12.56
사각형을 그려요
None
12

 

special method

dir(Shape)
['__abstractmethods__',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_abc_impl',
 'draw']