일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- namedtuple
- Numpy data I/O
- 딥러닝
- linalg
- VSCode
- Array operations
- pivot table
- Python
- boolean & fancy index
- 정규분포 MLE
- subplot
- 부스트캠프 AI테크
- scatter
- Comparisons
- Operation function
- python 문법
- unstack
- Python 유래
- 가능도
- ndarray
- Python 특징
- type hints
- dtype
- groupby
- 카테고리분포 MLE
- 표집분포
- seaborn
- Numpy
- 최대가능도 추정법
- BOXPLOT
- Today
- Total
또르르's 개발 Story
[04] Python의 OOP (객체 지향 프로그래밍) 와 module (모듈) 본문
대부분의 언어는 객체 지향 프로그래밍이 가능하고 프로그래밍을 위해 Class를 지원합니다.
Python 또한 Class가 있으며 다른 언어보다 강력한 객체 지향 프로그래밍을 지원합니다.
모듈은 class로 만들어진 조각으로 다른 개발자가 만들어놓은 라이브러리를 자신의 프로그램에 이식할 수 있습니다.
오늘은 Python의 객체 지향 프로그래밍과 모듈에 대해 알아보는 시간을 갖겠습니다.
1️⃣ 객체 지향 프로그래밍 개요
객체 지향 프로그래밍은 Object-Oriented Programming (OOP)라고 불립니다. 말 그대로 객체들이 중심이 된 프로그래밍 기법인데요. 객체란 하나의 역할을 수행하는 '메소드와 변수(데이터)'의 묶음이라고 생각하면 됩니다. OOP는 이러한 객체 개념을 프로그램으로 표현한 것인데요. 속성은 변수(variable), 행동은 함수(method)로 표현됩니다.
예를 들어 "축구 프로그램"을 프로그램으로 작성한다고 가정할 때
객체 종류는 팀, 선수, 심판, 공 등이 될 수 있습니다.
이러한 객체들의 Action(함수)들은 선수는 "슛하다", "패스하다"와 같은 행동이 있고, 심판은 "경고를 주다", "경기 종료하다"와 같은 행동들이 있죠.
Attribute(속성)은 객체가 가지고 있는 특징인데 선수의 경우는 "선수 이름", "포지션" 등을 가질 수 있고, 팀의 경우 "팀 이름", "팀 소속 선수" 등의 속성을 가질 수 있습니다.
이러한 객체들은 필요할 때마다 새로 만드는 것이 아닌 클래스(class)라는 설계도를 가질 수 있습니다. 따라서 사용자는 클래스라는 설계도에 따라 객체를 생성할 수 있습니다. (실제 구현된 객체는 인스턴스(instance)라는 이름을 가집니다.)
2️⃣ 클래스 (class)
클래스는 Python에서 아래와 같은 형태로 만들어집니다.
class SoccerPlayer(object):
def __init__(self, name, position, back_number):
self.name = name
self.position = position
self.back_number = back_number
def change_back_number(self, new_number):
print("선수의 등번호를 변경합니다 : From %d to %d" % (self.back_number, new_number))
self.back_number = new_number
첫 줄에 class SoccerPlayer()의 parameter는 상속받는 객체명을 뜻합니다.
default값으로는 object가 들어가서 상속받지 않는 경우 써주지 않아도 됩니다.
class SoccerPlayer(object):
이후, Attribute를 선언할 수 있습니다. Attribute 추가를 하기 위해서는 __init__을 사용하는데, __init__은 객체 초기화 예약 함수로써 instance가 생성되는 동시에 실행되는 method입니다.
따라서 self.name, self.position, self.back_number이라는 attribute를 객체는 가질 수 있으며, __init__의 parameter를 통해 attribute를 초기화할 수 있습니다.
여기서 self는 생성된 instance 자신을 가리키며 class의 attribute와 method의 parameter에 써주셔야합니다. 그래야 class에 속해있다는 것을 알 수 있으니까요!
def __init__(self, name, position, back_number):
self.name = name
self.position = position
self.back_number = back_number
method 구현은 일반 함수와 같으나, 첫 번째 parameter에 self를 추가해야합니다.
def change_back_number(self, new_number):
print("선수의 등번호를 변경합니다 : From %d to %d" % (self.back_number, new_number))
self.back_number = new_number
재밌는 것은 __init__과 같은 특수한 예약 method들이 있습니다.
예약 method에는 __main__이나 __add__, __str__, __eq__ 등이 있는데 이러한 함수를 사용하면 특정한 일을 수행합니다.
많이 사용하는 __str__을 예시로 들자면, instance 자체를 print 하게 되면 __str__의 return 값이 출력되게 만들어져 있습니다.
def __str__(self):
return "선수의 이름은 %s 입니다." % (self.name)
>>> son = SoccerPlayer("son", "FW", 7)
>>> print(son)
선수의 이름은 son 입니다.
3️⃣ OOP 특성
그렇다면 OOP의 특성은 뭐가 있을까요. OOP는 상속, 다형성, 가시성 3가지의 특성을 가지고 있습니다.
1) 상속 (Inheritance)
상속은 부모 클래스로부터 attribute와 method를 물려받아 자식 클래스에서 사용할 수 있는 것입니다.
먼저 부모 클래스와 자식 클래스가 뭔지 알아야겠죠?
클래스는 실제 세상을 모티브 했고 클래스끼리는 서로서로 연관이 있습니다. 예를 들어 Shape(모양)이라는 class를 선언했다고 할 때 모양에는 삼각형, 사각형, 정사각형 등 여러 가지 모양이 있습니다. 이러한 경우 모양이라는 클래스에서 파생해서 삼각형 class, 사각형 class, 정사각형 class를 생성할 수 있습니다. 이때 "모양"은 부모 클래스, "삼각형", "사각형", "정사각형" class는 자식 클래스라고 할 수 있습니다.

그렇다면 상속은 부모 클래스가 가지고 있는 attribute와 method를 그대로 받아 자식에서 사용할 수 있다는 것인데 Circle, Rectangle, Square class는 Shape에서 선언했던 draw()라는 메서드를 그대로 사용할 수 있습니다.
아래 코드는 Person class는 부모 클래스이며, Korean은 자식 클래스입니다. Korean 클래스는 아무것도 만들어지지 않았음에도 불구하고 name을 출력하면 값이 출력되는 것을 알 수 있습니다.

class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
class Korean(Person):
pass
>>> first_korean = Korean("Kims", 35)
>>> print(first_korean.name) # Korean은 실제 아무런 코드가 없어도 Person을 상속받았기 때문에 name을 출력할 수 있음
Kims
또한, super() 매소드를 사용하면 부모 객체를 불러올 수 있습니다. super()을 사용해서 부모 객체의 __init__과 attribute, method를 사용할 수 있습니다.
class Employee(Person): # 부모 클래스 Person으로 부터 상속
def __init__(self, name, age, gender, salary, hire_date):
super().__init__(name, age) # 부모객체 사용
self.salary = salary
self.hire_date = hire_date # 속성값 추가
def do_work(self): # 새로운 메서드 추가
print("열심히 일을 합니다.")
def about_me(self): # 부모 클래스 함수 재정의
super().about_me() # 부모 클래스 함수 사용
print("제 급여는 ", self.salary, "원 이구요, 제 입사일은 ", self.hire_date," 입니다.")
2) 다형성 (Polymorphism)
다형성은 상속된 클래스에서 같은 이름의 메서드를 다르게 작성할 수 있는 것입니다.
아래 그림과 같이 Shape은 Circle, Rectangle, Square로 나눠지는데 draw()라는 method를 각각 가지고 있습니다. 같은 이름의 method를 가지고 있더라도 해당 객체를 부르면 그 객체의 method가 호출됩니다.

Animal 클래스는 Cat과 Dog를 상속합니다. talk()라는 method는 Animal와 Cat, Dog 모두 가지고 있지만 (Animal는 구현을 안 했습니다) Cat과 Dog의 객체는 각자의 talk() 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())
Missy: Meow!
Mr. Mistoffelees: Meow!
Lassie: Woof! Woof!
3) 가시성 (Visibility)
가시성은 캡슐화 또는 정보 은닉이라고 할 수 있습니다.
Class를 설계할 때, 상대방이 변수에 접근해서 값을 마음대로 바꾸는 것을 방지하기 위해 만들었습니다.
만약 변수를 다른 사람이 접근 못하게 하고 싶다 하면 아래와 같이 변수명 앞에(self. 뒤에) __를 붙여주시면 됩니다.
이 것을 함수명 변경(맨글링)이라고 합니다.
self.__items = []
이렇게 되면 Class 밖에서 items 변수를 직접적으로 호출하지 못합니다.
하지만 property decorator를 사용하면 숨겨진 변수의 반환이 가능합니다.
@property
def items(self):
return self.__items
>>> items = my_inventory.items # 이런식으로 사용이 가능합니다.
❗ Peer Session
Python에서 " __ "를 변수 앞에 붙이는 것을 맨글링이라고 하는데 맨글링은 Java와 같이 Private의 느낌보다는 함수명을 변경하는 느낌입니다. Private은 변수를 함수 밖에서 호출하지 못하게 하지만, 맨글링은 변수명이 변경되어 호출을 막는(?) 것입니다.
예시로 Inventory라는 class에서 items를 맨글링 변수로 만들고 객체의 요소들을 보면 '_Inventory__items'가 있는 것이 보입니다. 이 값이 바로 __items의 바뀐 변수명입니다.
class Inventory(object):
def __init__(self):
self.__items = []
def print(self):
return self.__items
>>> temp_inventory = Inventory()
>>> print(dir(temp_inventory))
['_Inventory__items', '__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__']
따라서 '_Inventory__items' 변수에 값을 append 하고 print() method로 출력하면 값이 들어간 것을 알 수 있습니다.
>>> temp_inventory._Inventory__items.append(1)
>>> print(temp_inventory.print())
[1]
결론은 파이썬의 맨글링은 이름 그대로 private 하게 만드는 것보다 이름을 뭉개는 역할을 합니다.
4️⃣ Decorator
파이썬에서 함수 위에 @가 붙어있는 것을 본 적 있을 것입니다. 이 @의 용도는 바로 decorator입니다.
decorator에 대해 알기 전에 먼저 first-class objects와 inner function에 대해 알아야 합니다.
1) first-class objects (일급 객체/함수)
파이썬의 함수는 일급 함수입니다. 이 뜻은 바로 변수나 데이터 구조에 함수를 할당할 수 있다는 것입니다.
함수를 변수나 데이터 구조에 넣을 수 있는 함수는 "일급 함수"라고 합니다.
물론 객체도 마찬가지입니다.
def square(x):
return x * x
>>> f = square # f라는 변수에 함수 삽입 가능
2) Inner function (내부 함수)
파이썬은 함수 안에 함수를 선언하는 것이 가능합니다.
def print_msg(msg):
def printer():
print(msg)
printer()
>>> print_msg("Hello, Python")
Hello, Python
Closures 함수 (클로저 함수)는 inner function을 return값으로 반환하는 함수를 말합니다.
def print_msg(msg):
def printer():
print(msg)
return printer()
>>> another = print_msg("Hello, Python")
>>> another
Hello, Python
Python은 decorator를 사용해서 클로저 함수를 간단하게 사용할 수 있습니다.
printer의 parameter인 msg는 percent와 star의 inner의 *args로 전달되고, printer function은 percent의 paramter인 func에 전달됩니다.
def star(func): # func은 printer가 들어가있는 percent를 나타냅니다.
def inner(*args, **kwargs): # *args는 "Hello"를 받습니다.
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return inner
def percent(func): # func은 아래있는 printer를 나타냅니다.
def inner(*args, **kwargs): # *args는 "Hello"를 받고, **kwargs는 값이 없습니다.
print("%" * 30)
func(*args, **kwargs)
print("%" * 30)
return inner
@star
@percent # percent함수를 먼저 불러온 후 star 함수를 불러옵니다.
def printer(msg): # parameter는 percent의 inner와 star의 inner *args로 넘겨집니다.
print(msg)
>>> printer("Hello")
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
즉, 피 function 자체(printer 함수)는 decorator의 parameter (percent의 parameter)로 들어가고, 피 parameter (printer함수의 parameter)는 inner함수의 parameter (inner의 parameter)로 삽입됩니다.
그래서 inner function에만 *args와 **kwargs가 전달되는지 궁금해서 아래와 같이 해보니까 출력이 안됩니다.
def star(func, *args): # 값 *args의 전달이 안됩니다.
print("star : ", *args)
def inner(*args, **kwargs):
print("*" * 30)
func(*args, **kwargs)
print("*" * 30)
return inner
def percent(func, *args): # 마찬가지로 값 *args의 전달이 안됩니다.
print("percent : ", *args)
def inner(*args, **kwargs):
print("%" * 30)
func(*args, **kwargs)
print("%" * 30)
return inner
@star
@percent
def printer(msg):
print(msg)
>>> printer("Hello")
percent : # 이부분이 빈 것을 알 수 있습니다.
star :
******************************
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Hello
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
******************************
decorator에 argument 값을 줄 수도 있습니다. 점점 복잡해집니다.
이 경우에는 raise_two function이 첫 번째 inner 함수(wrapper 함수)에 들어가고 raise_two parameter가 두 번째 inner 함수 (inner 함수)에 들어갑니다.
즉, decorator에 argument 값이 있으면 inner가 한 단계 더 삽입됩니다.
def generate_power(exponent): # decorator의 argument가 있을 때 argument는 exponent로 들어갑니다.
def wrapper(f): # 첫 번째 inner 함수 (wrapper)에 function이 들어갑니다.
def inner(*args): # 두 번째 inner 함수 (inner)에 parameter가 들어갑니다.
result = f(*args) # n=7이고 7^2 = 49가 result에 들어갑니다.
return exponent**result # 2^49 = 562949953421312
return inner
return wrapper
@generate_power(2)
def raise_two(n):
return n**2
>>> print(raise_two(7))
562949953421312
5️⃣ Module
모듈은 class로 만들어진 조각으로 다른 개발자가 이미 만들어놓은 내용을 자신의 프로그램에 쉽게 이식할 수 있습니다.
따라서 프로그램을 모듈화 시키면 다른 프로그램에서 사용하기가 쉬워집니다.
패키지는 모듈을 모아놓은 단위이며, 하나의 프로그램입니다.
모듈은 import 문을 사용해서 module을 호출할 수 있습니다. 아래 코드처럼 fah_converter.py를 import 하면 fah_converter.py안에 있는 method, class 등 모든 object를 메모리에 올리게 됩니다.
- fah_converter.py
# fah_converter.py
def covert_c_to_f(celcius_value):
return celcius_value * 9.0 / 5 + 32
- module_ex.py
# module_ex.py
import fah_converter # import를 사용해서 위에서 만든 함수 값을 가지고옵니다.
print ("Enter a celsius value: ")
celsius = float(input())
fahrenheit = fah_converter.covert_c_to_f(celsius) # fah_converter.py에 있는 conver_c_to_f 함수를 사용할 수 있습니다.
print ("That's ", fahrenheit, " degrees Fahrenheit")
추가로 .py 파일을 만들게 되면 __pycache__라는 폴더가 만들어지는데 이 __pycache__ 폴더는 모듈을 import 할 때 만들어지는 컴파일된 파일들을 저장합니다.
또한, 모듈을 호출할 때 여러 가지 방법이 있습니다.
1) Alias 설정하기 - 별칭 사용
import fah_converter as fah # fah_converter를 fah라는 이름으로
>>> print(fah.convert-c_to_f(41.6)) # fah. 으로 함수를 사용할 수 있습니다.
2) 모듈에서 특정 함수 또는 클래스만 호출
from fah_converter import covert_c_to_f
>>> print(convert_c_to_f(41.6)) # 함수만 호출할 수 있습니다.
3) 모듈에서 모든 함수 또는 클래스 호출
from fah_converter import *
>>> print(convert_c_to_f(41.6)) # 전체를 호출할 수 있습니다.
from, import를 사용하면 함수를 사용할 때 코드가 간략해지지만, 다른 사람이 내 코드를 볼 때 어디서 가지고 온 함수인지 구별하기 힘들다는 문제도 있습니다.
그리고 재밌는 사실은 import만 하면 class객체를 가지고 오지만, from, import를 하면 class 안에 있는 내용들을 복사해서 가지고 온다고 합니다.
모듈이 많아져 패키지 형태가 되면 from, import의 사용이 중요해집니다. 디렉토리 구분은 "."으로 구분하고, ".."는 부모 디렉토리를 나타냅니다.
from game.graphic.render import render_test() # 절대참조
from .render import render_test() # 현재 디렉토리 기준
from ..sound.echo import echo_test() # 부모 디렉토리 기준
6️⃣ 가상 환경 (Virtual Environment)
Python으로 프로젝트를 진행하다 보면 다양한 오픈소스 라이브러리를 사용하게 됩니다. 하지만 프로젝트마다 다른 라이브러리 사용과 작업환경이 다를 수 있고, 같은 환경에서 계속 작업하다 보면 필요 없는 모듈끼리 충돌이 일어날 수 있습니다. 이러한 불상사를 피하고자 Python에서는 가상 환경을 제공합니다.
가상 환경을 사용하면 프로젝트에 맞게 필요한 패키지만 설치할 수 있는데요. 인터프리터를 선택할 수 있는 것은 물론 프로젝트에 맞는 패키지를 설치할 수 있습니다.
하지만 패키지를 계속해서 다시 설치해야 하는 불편함도 있습니다. 그래서 이러한 불편함을 해결하고자 패키지를 한 번에 관리해주는 패키지 관리 도구를 사용합니다. Python의 대표적인 패키지 관리 도구는 virtualenv와 conda가 있습니다.
- Virtualenv + pip
가장 대표적인 가상 환경 관리 도구
레퍼런스 + 패키지 개수 - conda
상용 가상 환경 도구
설치의 용이성
하지만 요즘에는 pip보다 conda가 좋다고 합니다.
이유는 python이 c로 되어있는데 패키지를 다운로드할 때 어떤 파일들은 컴파일된 c파일이 필요합니다. 하지만 pip에는 컴파일된 c코드가 안 들어가 있는 경우가 있어 직접 다운받아야하는 수고로움이 있다고 합니다.
하지만 conda는 가상 환경을 지원하면서 컴파일된 c코드를 같이 지원하므로 더 편하다고 합니다.
오늘은 Python의 OOP와 모듈에 대해 정리해보았습니다.
다른 언어에도 있는 특징이지만 특히 Python에 특화된 특징들이 있었습니다.
Python은 역시 High Level 언어가 맞습니다!
'부스트캠프 AI 테크 U stage > 이론' 카테고리의 다른 글
[05] Python Exception/File/Log /data Handling (0) | 2021.01.22 |
---|---|
[04-1] Python 정규 표현식(Regular Expression) (0) | 2021.01.22 |
[03-1] Pythonic code (0) | 2021.01.20 |
[03] Python data structure (0) | 2021.01.20 |
[02] Python 기본 문법(변수, 함수, 문자열) (0) | 2021.01.19 |