본문 바로가기

Python

OOP, Module & Package

<OOP, Module & Package>

    7. Object-Oriented Programming

    8. Module & Package


Object-Oriented Programming

 

절차 지향 프로그래밍(Procedure Programming)의 문제점

 - 중간의 코드 수정 시, 모든 코드 수정이 필요할 수 있음

 - 협업 시, 한 프로그래머의 수정 사항이 다른 프로그래머에게 영향

 

따라서, 코드를 객체 단위로 나누어 작성 및 분업해야 한다.

객체 지향 프로그래밍

  • 객체 단위 코드 작성 및 분업
  • 각 Class당 Object가 하나만 존재하지 않는다
  • 그러나 Object의 Attribute는 달라도 Method는 동일하다

Class 선언부 (Class Declaration)

class Courier (object):    # (예약어) (클래스 명) (부모 클래스)

 

  • Class 명은 CamelCase가 관습적으로 사용
  • 부모 클래스가 지정되지 않았을 시, object가 자동 상속

 

Class 속성 (Class Attribute)

class Courier(object):
	NATIONALITY = 'KOR'
  • Class 전체가 공유하는 속성 값
  • 모든 instance가 같은 값을 참조
  • 남용하면 스파게티 코드의 원인이 됨
  • 클래스.attribute 형태로 접근
print(Courier.NATIONALITY)

 

 

Class 함수 (Method)

class Courier:
	def assign(self, parcel: str) -> None:
		self.parcels.append(parcel)
  • 각 객체에 적용이 가능한 함수
  • 현재 수정하고자 하는 객체를 self로 지칭(관습)

    - Python은 self를 첫 번째 파라미터로 명시적으로 받음

  • Class.method(instance, args, ...) 혹은 instance.method(args, ...) 형태로 접근
courier1.assgin('TV 상자')    # 메소드 실행

 

 

객체 속성 (Class Attribute)

class Courier(obeject):
	def __init__(self, name: str, address: str):
		self.name = name
  • 각각의 instance가 개인적으로 가지는 값
  • instance.attr 형태로 접근
  • Class 형태로 선언되어 나온 객체는 언제 어디서든 attribute 수정 가능

 

 

Magic Method (__METHOD__)

생성자 (__init__)

class Courier(object):
	def __init__(self, name: str, address: str):
		self.name = name
		self.address = address
		self.parcels = []
  • instance를 생성할 때 호출
  • 일반적으로 instance의 속성을 초기화하는 데 사용
  • class(args, ...) 형태로 호출하여 객체 생성
  • 거의 유이하게 정해진 Argument format이 없는 Magic Method

 

소멸자 (__del__)

class Courier(object):
	def __del__(self):
		self.parcels.clear()
  • 객체를 소멸할 때 호출
  • 파이썬은 쓰레기 수거(Garbage Collection)로 메모리 관리

    - instance가 어디에서도 참조되지 않을 때 객체가 소멸

    - 소멸 타이밍을 잡기 어려워 잘 사용되지 않음

  • del 명령어

    - 변수 이름을 명시적으로 없애기 가능

    - 참조를 명시적으로 삭제하는 것이지 객체를 명시적으로 삭제하는 것이 아님

 

 

OOP의 3요소 - Inheritance, Visibility, Polymorphism

 

상속 (Inheritance)

  • 기존에 구현 틀 상속 → 새로운 틀 제작
  • 기존의 틀: 부모 Class, 새로운 틀: 자식 Class
  • 같은 기능을 재작성할 필요가 없음
  • 다중 상속 지원

    - 메소드 탐색 순서를 따름 (mro)

  • super 내장 함수를 이용하여 상위 Class 접근 가능
class Courier(object):    # (예약어) (클래스 명) (부모 클래스)

 

 

다형성 (Polymorphism)

  • 같은 이름의 메소드를 다르게 작성

    - 각 자식 Class가 다른 Class와 차별

    - 부모 메소드로 접근 시 자식 메소드 실행

    - 외부에서는 똑같은 API로 접근

    - 고객 입장에서는 코드 수정이 없음

  • Python에서는 동적 타이핑

    - 고객 입장에서는 클래스 구분 x

    - 같은 이름의 메소드가 있으면 실행 (Duck Typing)

 

 

Static & Class Method

  • Static Method

    - @staticmethod 꾸밈자 사용

    - 특별한 argument를 받지 않음

    - 일반적으로 Class 내 유틸 함수로 사용

    - Class를 일종의 Namespace로 사용

  • Class Method

    - @classmethod 꾸밈자 사용

    - 호출된 class인 cls를 받음 (self와 비슷)

    - factory 패턴에서 사용

 

 

Visibility

  • 다른 Class에게 Object의 내부를 감추기

    - 캡슐화, 정보 은닉

    - Class 간 간섭 최소화

    - 최소한의 정보만을 지정된 API로 공개

  • Python에서의 가시성

    - 명시적인 private&proceed 범위가 없음 → 모두 public

    - private 변수/함수 이름 앞에 "__"를 붙임 (밑줄 2개)

        ex) self.__name

    - protected 변수/함수 이름 앞에 "_"를 붙임 (밑줄 1개)

        ex) self._name

 

Property

  • Property를 통해 Getter, Setter를 명시적 설정 가능
  • Encapsulation 등에 활용
class Circle(object):
	PI * 3.141592

	def __init__(self, radius=3.):
		self.radius = radius

	@property                                    # Getter
	def area(self):
		return Circle.PI * self.radius ** 2
        
	@area.setter                                  # Setter
	def area(self, value):
		self.radius * (value / Circle.PI) ** 2

circle = Circle(5.)
print(circle.area)

circle.area = 10
print(circle.radius)

 

 

Indexing 메소드 (__getitem__, setitem__)

  • Indexing을 재정의
def __getitem__(self, index):                            # Indexing get
	return self.mapping.get(index, index * 2)

def __setitem__(self, index, item):                     # Indexing set
	self.mapping[index] = item

 

Length 메소드 (__len__)

  • Dataset 길이 반환
def __len__(self):                            # len(instance) 호출될 시 호출
	return len(self.data) * self.times

dataset = Dataset([10, 2, 5, 2], times=5)
print(len(dataset))	 # 20

 

 

Typing

  • instance를 다른 타입으로 형 변환할 때 호출
  • __str__, __int__, __float__, __bool__ 등이 존재
def __str__(self):        #str 형변환
	return self.address + ' 담당 ' + self.name 

text = str(courier)            # str 형변환 호출

 

Comparison Operator

  • 비교 연산자 호출

__lt__(B): A < B

__le__(B): A <= B

__gt__(B): A > B

__ge__(B): A >= B

__eq__(B): A == B

__ne__(B): A != B

 

def __lt__(self, other):            # '<' 연산자를 재정의
	return self.cid < other.cid

 

Arithmetic Operator

__add__: 더하기 연산자 호출 (Out-place 버전)

__iadd__: 더하기 연산 자 호출 (In-place 버전)

    → self를 직접 수정 필요 (self.real += other.real) 

__sub__: 빼기 연산자 호출

__mul__: 곱하기 연산자 호출

 

 

함수화 메소드 (__call__)

  • 생성된 객체를 호출 가능하게 만듦
  • instance(args, ...)가 instance.__call__(args, ...)를 호출
class AdditionNumber(object):
	def __init__(self, number: int):        # 생성자
		self.number = number

	def __call__(self, number: int):        # 함수화 메소드
		return number + self.number

addition_5 = AdditionNumber(5)
print(addition_5(10))                               # instance를 함수처럼 사용

 

Iterable

seq = list([1, 2, 3, 4, 5])

 

# 실제 for 문 내부 동작 방식

iterator = iter(seq)

while True:
    try:
        elem = next(iterator)

    except StopIteration:
        break

    print(elem)
  • iter 내장함수

    - 해당 객체의 순환자 반환

    - __iter__ 호출

  • next 내장함수

    - 해당 순환자를 진행

    - __next__ 호출

  • 끝에서 StopIteration Exception
  • Generator는 자동으로 __iter__와 __next__가 구현

 

Context Manager

class Courier:
	courier = Courier("김 기사")

	with courier:                         # enter()
		courier.parcels.append("소포")

	with courier("김 기사) as courier:     # exist() 
		courier.parcels.append("소포")
  • 소멸자 대용으로 특정 Block 입장/종료 시 자동으로 호출
  • File description 등을 자동으로 닫고자 할 때 사용

Module & Package

 

다른 파일의 함수를 가지고 오고 싶다면?

  • 다른 사람이 완성한 함수와 클래스 사용
  • 내장 & 외부 라이브러리 사용

모듈화가 필요

 

 

Import

  • 파이썬에서 모듈 == .py 파일
  • import 구문을 사용하여 모듈을 불러옴

    - 해당 파일 최상위에 선언된 모듈의 요소들을 불러오기 가능

    - module.element 식으로 사용

  • . 혹은 .. 없이는 절대 경로 기준 (Python이 실행되는 곳)
  • import 문은 import된 .py 파일을 처음부터 끝까지 실행
  • 해당 모듈을 main으로 했을 때 특정 Block을 실행시키고 싶을 시,
if __name__ == "__main__":
	print("이 코드는 functions 모듈이 메인일 때만 실행")

import directory.functions              # 폴더 내 .py 파일

import directory.functions as func      # as로 별칭 만들기

from directory import functions         # from으로 특정 부분 import

from directory.functions import add     # 특정 함수 import

from directory.functions import *       # *로 모두 import
  • 최상위에선 상대 경로가 작동되지 않음

    - 최상위를 거치는 경우 포함

    - 일반적으로 프로젝트 이름으로 폴더를 만들어 코드를 넣음

    - 부모 폴더 접근을 위해서는 모듈 형태로 실행 필요

python -m test_nlp.dir1.sub_main

    → main을 module 형태로 실행

  • '__init__.py': 폴더 import 시 초기화 가능

 

Conda를 이용하여 가상 환경 설정 (Virtual Enviromnet)

conda create -n 이름        # 가상 환경 만들기

conda activate 이름          # 가상 환경 활성화

conda deactivate             # 가상 환경 나가기

conda install <패키지 이름> -c <설치 채널>        #패키지 설치

    - 채널이 명시되어 있지 않을 경우 default 채널 탐색

    - 채널은 문서를 참고하거나 Anaconda Hub에서 검색

'Python' 카테고리의 다른 글

Colab Runtime 유지  (0) 2022.08.01
I&O, Setting & Exception & Logging, Web  (0) 2022.05.01
Advanced Data Structure & String  (0) 2022.04.28
Python Programming  (0) 2022.04.26
Python Basic  (0) 2022.04.25