본문 바로가기
NLP/AI 이론

[Python] OOP + 파이썬 모듈화

by ㅣlㅣl 2024. 1. 28.

네이버 부스트코스에서 제공하는 최성철 님의 강의를 참고하여 작성된 포스팅입니다.


 

객체 지향 프로그래밍 (Object-Oriented Programming)

만들어 놓은 코드를 재사용하기 위해 클래스, 객체를 다룬다

  • 객체 : 속성 (variable), 함수(method) 로 구성됨
  • 파이썬 역시 객체지향 언어
  • class : 설계도, 틀
  • instance : class로 구현한 실체
class SoccorPlayer(object): # object : 상속받는 객체명
    def __init__(self, name, back_number):
        self.name = name
        self.back_number = back_number

    def change_back_number(self, new_number):
        print(f'선수 등번호 변경 {self.back_number} to {new_number}')
        self.back_number = new_number

클래스 밖에서도 속성 값 변경이 가능하나 이를 권장하지는 않음


  • 함수 작성 스타일
    • snake_case : 띄어쓰기 부분에 ‘_’, 함수/변수명
    • CamelCase : 띄어쓰기 부분에 대문자, class 명

 

attribute

 

method

기존 함수와 같이 추가하되, 반드시 파라미터 첫 번째 인자에 ‘self’를 추가해야 클래스 함수가 됨

 

 

객체 지향 언어의 특징 및 구성요소

상속 (Inheritance)

부모 클래스로부터 속성, 메서드를 물려받은 자식 클래스 생성

 

# 초기 상속 default : object
class Person(object):
	def __init__(self, name, age):
		self.name = name
		self.age = age
	def about_me(self):
		print(f"이름은 {self.name}, 나이는 {self.age}")

class Korean(Person): # person 클래스로부터 상속받음
	pass

first_korean = Korean("Sungchul", 35)
print(first_korean.name) # "Sungchul", 부모 클래스에서 상속받은 값 그대로

 

새로운 메서드 추가나 부모 클래스 함수 재정의도 가능

 

class Employee(Person):
	def __init__(self, name, age, gender, salary, hire_date):
		super().__init__(name, age, gender) # 부모 객체를 사용
		self.salary = salary
		self.hire_date = hire_date
	
	def do_work(self):
		print("일하기")
	
	def about_me(self): # 부모 클래스 함수 재정의
		print(f"이름 {self.name}, 급여 {self.salary}, 입사일 {self.hire_date}")

 

 

다형성 (Polymorphism)

같은 이름 메소드의 내부 로직을 다르게 작성

Dynamic Typing 특성으로 인해 파이썬에서는 같은 부모 클래스의 상속에서 주로 발생

💡 개념은 같은데 세부적인 구현이 다를 때!

함수 명은 같은데 안의 인터페이스 ,코드에 따라서 조금씩 다르게 코드를 짤 수 있는 것

# 상위 클래스인 Animal 에서 abstract method만 구현
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())

 

가시성(Visability)

객체의 정보를 볼 수 있는 레벨을 조절하는 것

임의의 사용자가 정보를 수정하는 것을 방지하거나 제품 판매 시 소스 보호

 

캡슐화 (Encapsulation)

클래스 설계 시 클래스 간 간섭/ 정보공유 최소화

인터페이스만 알면 쓸 수 있음

개념적으로 정보를 분리 → 코딩 실수 줄여

 


 

다음과 같은 상황을 가정해보자.

  • Product 객체를 Inventory 객체에 추가
  • Inventory에는 오직 Product 객체만 들어감
  • Inventory에 Product가 몇 개인지 확인이 필요
  • Inventory에 Product items 접근 불가
class Product(object):
    pass
class Inventory(object):
    def __init__(self):
				# 언더바 두개 : private 변수
        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())
my_inventory.add_new_item(Product())
print(my_inventory.get_number_of_items())
print(my_inventory.__items)
my_inventory.add_new_item(object)


#################
new item added
new item added
2
Traceback (most recent call last):
  File "c:\Users\hwyew\Downloads\boostcamp\test2.py", line 22, in <module>
    print(my_inventory.__items)
          ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Inventory' object has no attribute '__items'

이런 식으로 작성하면 밖에서 product items에 직접 접근이 불가함

 

 

다음과 같은 상황을 가정해보자.

  • Product 객체를 Inventory 객체에 추가
  • Inventory에는 오직 Product 객체만 들어감
  • Inventory에 Product가 몇 개인지 확인이 필요
  • Inventory에 Product items 접근 허용
class Product(object):
    pass

class Inventory(object):
    def __init__(self):
        self.__items =[]

    @property
    def items(self):
        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())
my_inventory.add_new_item(Product())
print(my_inventory.get_number_of_items())

items =my_inventory.items
items.append(Product())
print(my_inventory.get_number_of_items())


#######################
new item added
new item added
2
3

@property 데코레이터 이용하면 외부에서는 접근이 안되지만, 내부에서는 접근이 가능해서 반환할 수 있게 해줌

단, 그대로 반환하면 외부에서 값 수정도 가능해지므로 보통은 copy해서 반환

 

 

decorate

  • first-class objects : 일등 함수, 일급 객체
    • 변수나 데이터 구조에 할당이 가능한 객체
    • 파라미터로 전달 가능 + 리턴 값으로 사용
    • 파이썬의 함수는 일급함수
    • map(f, ex) : 함수가 파라미터처럼 쓰임
def square(x):
    return x*x

f = square
f(5)
def square(x):
    return x * x
def cube(x):
        return x*x*x
def formula(method,argument_list):
    return [method(value)for value in argument_list]

 

  • inner function : 함수 내 또 다른 함수
    • closures : inner function을 return값으로 반환
      • 파이썬은 일급함수이기 때문에 함수 자체를 return 가능!
      • 비슷한 목적을 가진 함수를 다양하게 만들 수 있음
      • def print_msg(msg):
        	def printer():
        		print(msg)
        	return printer
        another =print_msg("Hello,Python")
        another()
        
        
        #################
        
        def tag_func(tag,text):
        	text =text
        	tag = tag
        	def inner_func():
        		return '<{0}>{1}<{0}>'.format(tag,text)
        	return inner_func
        
        h1_func=tag_func('title' ,"This is Python Class")
        p_func =tag_func('p' ,"DataAcademy")

 

  • decorator : 복잡한 클로저 함수를 간단하게 만들 수 있다
  • printer 함수가 star 함수 안에 들어감
def star(func):
	def inner(*args,**kwargs):
		print("*" *30)
		func(*args,**kwargs)
		print("*" *30)
	return inner

@star
def printer(msg):
	print(msg)
printer("Hello")
def generate_power(exponent):
	def wrapper(f):
		def inner(*args):
			result =f(*args)
			return exponent**result
		return inner
	return wrapper

@generate_power(2) # 값을 넣어줄 수도 있음
def raise_two(n): # wrapper(f)에 들어감
	return n**2

 

 

4-2. 모듈과 프로젝트

모듈을 모아 하나의 큰 프로그램 개발 → 다른 사람이 이해하고 사용하기 쉬워짐

패키지 : 모듈을 모아 놓은 단위, 하나의 프로그램

  • 파이썬에서는 모듈 == py 파일

 

namespace

  • 모듈을 호출할 때 범위를 정하는 방법
  • 필요한 내용만 골라서 호출 (from ~ import ~)
  • 독자 입장에서는 읽기 힘들 수 있음. 어디서 import 했는지 명시해주는 게 좋을 수 있다.
  • alias 설정 가능 (import ~ as ~)
  • 좋은 코드를 위해서는 import * 하는 습관을 줄이자! 

 

 

package

하나의 대형 프로젝트를 만드는 코드 묶음

  • 다양한 모듈들의 합, 폴더로 연결됨
  • init, main 같은 키워드 파일명 사용됨
  • 폴더별로 **init.py 구성**
    • 현재 폴더가 패키지임을 알리는 초기화 스크립트 → 없을 경우 패키지로 간주하지 않음 (3.3+부터는 없어도 되긴 하지만 관습적으로 함)
    • 하위 폴더와 py 파일 (모듈)을 모두 포함
    • import, all 키워드 사용
    • # game/init.py -> 각 폴더마다 생성 
      __all__=['image','stage','sound'] # 앞으로 쓸 하위폴더 모듈들의 이름을 적음
      
      # 전부 호출
      from . import image
      from . import stage
      from . import sound
      
      # image/init.py
      __all__=['character','object_type']
      
      # 전부 호출
      from . import character
      from . import object_type
  • main.py 만들기 : 폴더 자체를 불러와서 패키지 이름만으로 호출 가능
  • from stage.main import game_start
    from stage.sub import set_stage_level
    from image.character import show_character
    from sound.bgm import bgm_play
    if __name__=='__main__':
    	game_start()
    	set_stage_level(5)
    	bgm_play(10)
    	show_character()
  • 구성 이후에는 패키지 이름만으로 호출 가능

 

 

패키지 namespace

  • 절대 참조
    • from game.graphic.render import render_test()
  • 상대 참조
    • # . : 현재 디렉토리 기준 # .. : 부모 디렉토리 기준 from .render import render_test() from ..sound.echo import echo_test()

'NLP > AI 이론' 카테고리의 다른 글

[AI Math] 벡터와 행렬의 개념  (1) 2024.01.28
[Python] NumPy & Pandas  (1) 2024.01.28
[Python] Python Data Handling  (1) 2024.01.28
[Python] Python File/Exception/Log Handler  (1) 2024.01.28
[Python] 파이썬이란?  (1) 2024.01.28