매일공부

[AI 기초 다지기] 파이썬 class와 instance 본문

Programming/Python

[AI 기초 다지기] 파이썬 class와 instance

aram 2022. 8. 1. 22:20

- 객체지향 프로그래밍 Object-Oriented Programming, OOP

  • 객체: 실생활에서 일종의 물건 속성(Attribute)와 행동(Action)을 가짐
  • OOP는 속성 = 변수(variable)  /  행동 = 함수(method)로  표현
     > 수강신청 프로그램을 작성한다면, 교수·학생·관리자과목입력·수강신청 행동 수강과목·강의과목을 중심으로 프로그램을 각각 작성 후 연결하는 기법
  • Python = 객체 지향 프로그램 언어
  •  OOP 구성
    • class 클래스 = 설계도(데이터 유형)
      > 구성: 속성과 기능(동작)메서드 + 속성을 초기화하는 메서드
      > 목적: 객체를 생성하는 설계도, 객체를 메모리 생성하기 위해서
    • instance 인스턴스(객체) = 실제로 사용하는 구현체(데이터를 가지고 있는 변수)
  • 객체지향 언어의 3대 특성
     : 상속다형성(override 재정의), 캡슐화(타 프로그램에 쉽게 조립 및 분해 가능)

 

- Objects in Python

  • class 선언: object - python3에서는 안 적어도 자동 상속
    • python의 모든 class는 object를 상속 받
    • 명시적 선언을 하지 않으면 object를 상속받음
    • object : 모든 python의 객체가 갖춰야하는 속성과 메서드가 정의되어 있음 
class SoccerPlagyer ( object ) :
class 예약어 class 이름   상속받는 객체명  
  • Python naming rule
  띄워쓰기 부분 사용 유래
snake_case "_" 추가 파이썬 함수/변수  뱀 처럼 늘여쓰기
CamelCase 대문자 파이썬 Class 낙타의 등 모양

 http://bit.ly/3aP5Yjh

  • Attribute 추가
    • def __int__(self, 속성정보) :
    • __int__() : 객체 초기화 예약 함수(생성자 constructor)
                      : class에서 사용할 변수를 정의하는 함수
    • __ : 특수한 예약 함수나 변수 / 함수명 변경(맨글링)으로 사용
      > __new__(), __init__()__main__ , __add__ , __str__ , __eq__
      > 행동 메소드 = Magic Method, Special Method
    • __new__(cls, *args, **kwargs)
      : 클래스(cls)의 인스턴스를 만듦
      : 클래스(cls)로부터 "빈 객체 생성" - 부모의 __new__()사용
      :  __init__()보다 먼저 실행됨
    • __del__()
      :
      소멸자(Destructor), 인스턴스가 사라질 때 호출
      : 리소스 해제 작업 시 사용됨
    •  매직 매소드 속성 https://corikachu.github.io/articles/python/python-magic-method
class SoccerPlayer(object):
    #[클래스변수 = 초기값]
    def __init__(self, name, position, back_number):
    	#self.인스턴스변수 = 초기값   > 생성된 객체의 인스턴스변수(속성) 초기화
        self.name = name 
        self.position = position 
        self.back_number = back_number
        
#객체 생성
jin = SoccerPlayer("Jin", "MF", 10)
print(jin)
<__main__.SoccerPlayer object at 0x00000218EE56BD90>
#모듈을 선언한 적이 없기 때문에 실행환경의 기본이름 = __main__
 ☞ 이렇게만 있으면 함수 호출했을 때 주소가 반환됨
class SoccerPlayer(object):
    def __init__(self, name, position, back_number): #생성된 객체의 인스턴스 변수(속성) 초기화
        self.name = name 
        self.position = position 
        self.back_number = back_number
        
    def __str__(self): #str함수도 있으면 호출했을 때 주소가 아닌 str값 반환
        return "Hello, My name is %s. I play in %s in center " %(self.name, self.position)

    def __add__(self, other):  #연산자 오버로딩 Operator Overloading
        return self.name + other.name

jin = SoccerPlayer("Jin", "MF", 10)
print(jin) #Hello, My name is Jin. I play in MF in center 
park = SoccerPlayer("park", 'WF', 13)
print(jin + park) #Jinpark

 

  • method(Action) 구현
    • 추가는 기존 함수와 동일
    • but 반드시 self 추가해야함 class 함수로 인정
    • self 란?
      : 클래스에서 생성된 인스턴스에 접근하는 예약어
      : 생성된 instance 자신을 의미.
      즉, 코드 안에서는 self / 밖에서는 변수명으로 불림
  • objects(instance) 이름 선언과 함께 초기값 입력
jin = SoccerPlayer ("Jin", "MF", 10)
객체명   Class명 __init__ 함수 Interface, 초기값
class SoccerPlayer(object):
    def __init__(self, name: str, position: str, back_number: int): #변수 옆에 hint 줄 수 있음
        self.name = name 
        self.position = position 
        self.back_number = back_number
        
    def __str__(self): #object로부터 상속받아서 재정의(override)
        return "Hello, My name is %s. I play in %s in center " %(self.name, self.position)

    def __add__(self, other):  #이걸 사용하면 두개를 더하거나 빼기, 곱하기 등 가능 
        return self.name + other.name
    
    def change_back_number(self, new_number):
        print('선수의 등번호를 변경합니다 : From %d to %d' % (self.back_number, new_number)) 
        self.back_number = new_number

# SoccerPlayer를 사용하는 instance 코드
jin = SoccerPlayer("Jin", "MF", 10) #현재 선수의 등번호는 : 10
print("현재 선수의 등번호는 :", jin.back_number) #객체.속성  > 속성(인스턴스 변수) 참조

jin.change_back_number(5) #객체.메서드() : 메서드 호출해서 등번호 변경
print("현재 선수의 등번호는 :", jin.back_number)
#선수의 등번호를 변경합니다 : From 10 to 5
#현재 선수의 등번호는 : 5

names = ['김', '박']
positions = ['striker', 'goalkeeper']
numbers = [7, 10]

# 클래스-인스턴스 
player_objects = [SoccerPlayer(name, position, number) for name, position, number in zip(names, positions, numbers)] 
print(player_objects[0]) #Hello, My name is 김. I play in striker in center.

 

  • 정적 메소드
    • @staticmethod 데코레이터로 선언
    • self 매개변수 없는 메소드
    • 인스턴스를 통해서 접근이 가능하지만 클래스를 통해 호출하는 것이 보통
    • 객체의 데이터 속성과는 관계가 없는 코드로 구현되는 것이 일반적
class Calculator:
    @staticmethod
    def plus(a,b):
        return a+b

Obj = Calculator()
Obj.plus(3, 5) #8

 

  • 클래스 메소드
    • @classmethod 데코레이터 cls 매개변수가 필요한 메소드
    • 클래스 속성을 다루기 위해 설계된 메소드
class InstanceCounter:
    count = 0  #클래스변수, 클래스로부터 생성되는 객체 개수 저장할 변수
    def __init__(self):
        InstanceCounter.count += 1 #클래스이름으로 클래스변수 참조 및 변경
    
    @classmethod
    def print_instance_count(cls):  #클래스 변수를 참조,출력하는 메서드
        print(cls.count)

a = InstanceCounter() #빈 객체 생성
InstanceCounter.print_instance_count() #InstanceCounter클래스로부터 생성된 객체개수 출력
#출력 : 1
b = InstanceCounter()
InstanceCounter.print_instance_count() #출력 : 2

 

- OOP Implementation Example

  • 구현 가능한 노트북 프로그램 만들기
    • Note 정리하는 프로그램
    • Notebook > add_note, remove_note, get_number_of_pages  //  title, page_number, notes
    • Note > write_content, remove_all  //  content
더보기
class Note(object):
    def __init__(self, contents = None):  
        self.contents = contents

    def write_contents(self, contents):
        self.contents = contents

    def remove_all(self):
        self.contents = ""

    def __str__(self):
        return self.contents

class Notebook(object):
    def __init__(self, title):  
        self.title = title
        self.page_number = 1  ###page_number : 노트북이  실제 저장하고 있는 노트 인덱스
        self.notes = {}   #page 는 키로 note 객체는 value로 저장        
          
    def add_note(self, note, page_num=0):  #page_number 는 note 객체를 저장할 key
        if len(self.notes.keys()) < 301:   #저장된 note개수를 체크
            if page_num==0 :               ###page_num : 내가 추가, 삭제 하고 싶은 페이지
                self.notes[self.page_number]=note
                self.page_number +=1   
                print("노트를 notebook에 추가했습니다.")
            else :
                if page_num not in self.notes.keys() :
                    self.notes[page_num] = note   
                    print("노트를 notebook에 추가했습니다.")
                else  :
                    print("해당 페이지에는 이미 노트가 존재합니다.")
        else :
             print("더 이상 노트를 추가하지 못합니다.")

    def remove_note(self, page_num):  #key로 전달된 페이지번호에 저장된 note 객체 삭제
        if page_num in self.notes.keys() :
            del self.notes[page_num]
            print(page_num,"에 노트가 삭제되었습니다.")
        else :
            print(page_num, "에 노트가 존재하지 않습니다.")

    def get_number_of_all_pages(self):   # notebook에 저장된  note 객체 개수 반환 (저장된 notes의 페이지수 반환)
        return len(self.notes.keys())
    
    def get_note_of_page(self, page):
        if page in self.notes.keys() :
            return self.notes[page]
        else :
            print(page,"페이지에 note가 존재하지 않습니다.")

 

- 객체지향 언어의 특징 OOP characteristics

  • 실제 세상을 모델링
  • 상속(Inheritance)
    • 부모클래스로부터 속성과 Method를 물려받은 자식 클래스를 생성하는 것
    • super() : 부모 클래스에 정의되어 있는 메소드 호출
class A :     #object 자동 상속받음,  B의 부모클래스, Super 클래스, Base 클래스
    def __init__(self):    # 생성된 객체의 초기화 
        print("A.__init__() called ")
        self.message = "Made in A"      #인스턴스 변수 정의 및 초기화

    def action(self):
        print("A.action called ")

class D(A) :   #D는 A의 자식클래스, Child, Sub클래스 , Drived클래스
    def __init__(self):    # 생성된 객체의 초기화 
        super().__init__()   #super로 넘겨받을 땐 self 사용하면 안 됨 > 그대로 상속 받아 확인가능
        print("D.__init__() called ")
        
    def action(self):  #부모 A로부터 상속받은 메서드를 "override 재정의"
        print("D.action called ")
        super().action()  #부모로부터 상속받은 action() 호출

obj3 = D()     #객체 생성
print(obj3.message)   #상속받은 message  멤버 변수 참조
obj3.action()   #상속받은 메서드를 "재정의override한 메서드 호출"

#A.__init__() called 
#D.__init__() called 
#Made in A
#D.action called 
#A.action called

 

  • 다중 상속 : 부모 클래스의 목록에서 가장 앞에 있는 클래스의 메소드를 물려줌 > 오버라이딩overriding
class A :     #object 자동 상속받음 
    def __init__(self):    # 생성된 객체의 초기화 
        print("A.__init__() called ")        

    def action(self):
        print("A.action called ")
        
class B :     #object 자동 상속받음 
    def __init__(self):    # 생성된 객체의 초기화 
        print("B.__init__() called ")        

    def action(self):
        print("B.action called ")        

class D (B, A) :
    def __init__(self):    # 생성된 객체의 초기화 
        print("D.__init__() called ") 

d = D()  #객체 생성
d.action() #가장 앞에서 상속 선언된 클래스의 메서드만 상속
#D.__init__() called 
#B.action called

 

  • 다형성(Polymorphism)
    • 이름은 같지만 내부 로직을 다르게 작성
    • 이름은 하나지만 다양하게 출력됨
class Animal:
    def __init__(self, name): # Constructor of the class
        self.name = name 
    def talk(self): # Abstract method, defined by convention only
        raise NotImplementedError("Subclass must implement abstract method")
    
class Cat(Animal):
    def talk(self):
        return 'Meow!'

class Dog(Animal):
    def talk(self):
        return 'Woof! Woof!'

animals = [Cat('Missy'), Cat('Mr. Mistoffelees'), Dog('Lassie')]
for animal in animals:
    print(animal.name + ': ' + animal.talk())
#Missy: Meow!
#Mr. Mistoffelees: Meow!
#Lassie: Woof! Woof!

 

  • 가시성(Visibility) > PrivateMember(은닉변수) : __변수명
    • 객체의 정보를 볼 수 있는 레벨을 조절하는 것
    • 누구나 객체 안에 모든 변수를 볼 필요 없음, 개인정보의 경우 무조건 내부에서만 다루어야 함.
    • Encapsulation
      : 캡슐화 또는 정보 은닉 (Information Hiding)
      : Class를 설계할 때, 클래스 간 간섭/정보공유의 최소화
    • Product 객체를 Inventory 객체에 추가
    • Inventory에는 오직 Product 객체만 들어감
    • Inventory에 Product items는 직접 접근이 불가
class Product(object):
    pass


class Inventory(object):
    def __init__(self):  #객체 생성 후에 멤버변수(인스턴스 변수) 초기화
        self.__items = []
    
    @property #property decorator 숨겨진 변수를 반환하게 해줌
    def items(self):   #__items라고 하고 안되지만, items라고 하면 내부 정보 접근 가능
        return self.__items
        
    def add_new_item(self, product):
        if type(product) == Product:
            self.__items.append(product) 
            print("new item added") 
        else:
            raise ValueError("Invalid Item")
        
    def get_number_of_items(self):
        return len(self.__items)

my_inventory = Inventory()
my_inventory.add_new_item(Product())  #new item added

my_inventory.__items
#AttributeError: 'Inventory' object has no attribute '__items'
#객체의 메서드 내부가 아닌 외부에서 멤버변수(인스턴스 변수) 참조 불가능
# >> 아예 숨겨져 있으니까 없는 변수라고 error

 

- First-class objects

  • 일등함수 또는 일급 객체
  • 변수나 데이터 구조에 할당이 가능한 객체
  • 파라메터로 전달이 가능 + 리턴 값으로 사용
  • 파이썬의 함수는 일급함수
  • map(f, ex) > f는 함수. 하지만 변수(파라메터)처럼 쓰임 > 이런 것이 일급함수

 

- Inner function

  • 함수 내에 또 다른 함수가 존재
  • closures
    : inner function을 return값으로 반환 
       > 즉, return 값에 상관없이 inner함수가 항상 리턴되는 것 
def tag_func(tag, text):
    text = text 
    tag = tag
    def inner_func(): #return값과 관계없이 항상 출력됨 = closure
        return '<{0}>{1}<{0}>'.format(tag, text)
    return inner_func

h1_func = tag_func('title', "This is Python Class") 
p_func = tag_func('p', "Data Academy")
print(h1_func()) #<title> This is Python Class <title>
print(p_func()) #<p> Data Academy <p>

 

- decorator function

  • 복잡한 클로져 함수를 간단하게
def star(func):
    def inner(*args, **kwargs):
        print("*" * 30) 
        func(*args, **kwargs) 
        print("*" * 30) 
    return inner
   
@star #decorator로 간단하게 호출
def printer(msg):
    print(msg) 
printer("Hello")

 

728x90
Comments