
[25/4/30 수업 정리본]
파이썬을 기반으로 OOP를 얕게(?) 훑고 파이썬의 데이터 타입을 주제로 수업이 진행됐다.
OOP를 얕게 훑었다고 했지만 워낙 양이 많아서 정리하는데 힘들었다 ^^;
그래도 새내기때 배우고 다 까먹어서 언젠간 다시 공부해야지 했던 내용이라 다시 복기하기 좋았다!
1. 캡슐화(Encapsulation)
관련있는, 또는 동일한 목적의 함수나 데이터를 묶어 외부로부터 내부를 감싸서 은닉하는 정보 은닉의 개념
2. 상속(Inheritance)
하위 클래스가 상위 클래스의 모든 속성을 물려받는 것 (이때 상위 클래스를 먼저 만드는게 아닌, 하위 클래스를 만들다보니 공통된 것이 생겨서 상위 클래스로 뽑아내는 것!)
3. 다형성(Polymorphism)
동일한 메시지에 대해 다르게 반응하는 성질로, mehod overring을 통해 구현한다.
4. 추상화(Abstraction)
객체들이 공통적으로 필요로 하는 속성이나 동작을 하나로 추출해 내는 작업
수업 자료에 추상화는 빠져있다.. 왜지? 뒤에 나오긴 함..
동적 바인딩
실행 시간 중에 일어나거나 실행 과정에서 변경될 수 있는 바인딩
함수와 메서드
함수는 아무나 부를 수 있는 반면, 메서드는 부르는 주체가 있다(클래스나 인스턴스만 부를수 있게 숨겨져 있다)
my_car = Car()
# 자료형 : Car class, 인스턴스 이름 : my_car
클래스를 기반으로 메모리를 할당하고, 할당된 메모리의 id값을 반환한다.
⚠️ 인스턴스를 생성하고 반드시 변수에 할당하여 메모리가 참조하게 해줘야 한다!
할당된 메모리가 인스턴스를 참조하지 않으면 garbage 상태가 되어 메모리가 초기화된다.
Garbage Collection : 메모리 관리 기법 중 하나로, 프로그램이 동적으로 할당한 메모리 영역 중 더 이상 사용되지 않는 메모리 공간을 자동으로 찾아 해제하는 기능
class Student :
def greeting(self) :
print("안녕하세요.")
def printName(self, name) :
print(f'이름은 {name}입니다.')
student = Student()
Student.greeting() # Unbound 호출
student.greeting() # Bound 호출
self.메서드이름(매개변수)클래스이름.메서드이름(인스턴스, 매개변수) -> Unbound 호출인스턴스이름.메서드이름(매개변수) -> Bound 호출클래스나 인스턴스 내부에 데이터를 저장하기 위해서 만든 변수
*메서드 안에서 self 없이 선언되면 메서드의 지역변수
class Student :
schoolName = '상현초등학교'
def printName(self, name) :
self.num = 1
print(f'이름은 {name}입니다.')
student = Student()
# 속성 접근
print(f'클래스로 접근: {Student.schoolName} / 인스턴스로 접근 : {student.schoolName}')
# >> 클래스로 접근: 상현초등학교 / 인스턴스로 접근 : 상현초등학교
# 클래스를 이용해서 수정
Student.schoolName = '이현중학교'
print(f'클래스로 접근 : {Student.schoolName} / 인스턴스로 접근 : {student.schoolName}')
# >> 클래스로 접근 : 이현중학교 / 인스턴스로 접근 : 이현중학교
# 인스턴스를 이용해서 수정
student.schoolName = '풍덕고등학교'
print(f'클래스로 접근 : {Student.schoolName} / 인스턴스로 접근 : {student.schoolName}')
# >> 클래스로 접근 : 이현중학교 / 인스턴스로 접근 : 풍덕고등학교
# 인스턴스 속성 호출
student.printName('철수')
print(student.num)
# >> 이름은 철수입니다.
# >> 1
# 메서드를 호출하지 않고 해당 메서드 내부의 인스턴스 속성만 호출하면?
student2 = Student()
print(student2.num)
# >> AttributeError
특수 속성
__로 시작하고 __ 로 끝나는 속성
ex__doc__ : 함수를 설명하는 속성
ex__dict__ : 함수나 객체에 만들어진 Attribute 이름으로 쓰기 가능
==과 is
==
데이터를 비교하기 위한 연산자
a == b는a.\__ eq\__(b)의 편리 구문으로, 내장 자료형은 __eq__ 메서드를 오버라이딩해서 객체의 값을 비교
is
id의 일치 여부를 확인하는 연산자로, 연산자 오버로딩이 안됨
객체 지향 언어에서는 속성에 직접 접근하는 것을 권장하지 않기 때문에 속성에 접근하는 메서드를 별도로 만들어서 접근한다.
getter와 setter에 로깅 작업을 하는 코드를 심기도 한다.
생성자(초기화 메서드, __init__)
소멸자(__del__)
Garbage Collection
파이썬에서는 메모리가 자동으로 할당되고 자동으로 해제되기 때문에 특별히 사용자가 메모리 관리를 할 필요가 없다.
gc 동작 방식
모든 데이터에 reference count를 부여해서 참조횟수를 저장해놓는데 참조횟수가 0이면 메모리 해제 대상이 된다. 데이터의 참조를 변수에 대입하면 카운트가 1 증가하고 가리키는 변수가 소멸되거나 다른 참조를 대입하면 1이 감소한다.
*참조횟수를 확인하는 방법 : sys 모듈의getrefcount라는 함수에 객체를 대입
exsys.getrefcount(student1)
(이때 값은 1 증가해서 나옴, 이 함수 자체가 인스턴스를 참조하기 때문)
class Student :
# 클래스 속성 : 클래스와 인스턴스 모두 읽기 가능, 변경은 클래스를 이용해서만 가능
schoolName = '상현초등학교'
# 생성자(초기화 메서드)
def __init__(self, name='이름 없음') :
self.name = name
def getName(self) :
return self.name
def setName(self, name) :
self.name = name
def printName(self, name) :
self.num = 1
print(f'이름은 {name}입니다.')
# 소멸자
def __del__(self) :
print('인스턴스가 소멸될 때 호출됨')
student = Student()
print(student.getName())
# >> 이름 없음
# >> 인스턴스가 소멸될 때 호출됨
student = Student(name='yongmin')
print(student.getName())
student = None
# >> yongmin
# >> 인스턴스가 소멸될 때 호출됨
# ⚠️ student = None을 하지 않아도 소멸자가 호출되는데 이는 그냥 파이썬 프로그램이 종료되면서 호출되는 것
# ----------------------
student1 = Student()
student2 = student1
student1 = None
print("소멸되나?")
# 소멸되나?
# 인스턴스가 소멸될 때 호출됨
# 이 경우에는 student2가 인스턴스를 여전히 참조하고 있기 때문에 소멸자가 호출되지 않음(프로그램이 종료되서 한 번만 호출된 것)
# 따라서 이렇게 코드를 짜면 gc가 안되기 때문에 되도록이면 하지 말 것
Static Method
인스턴스를 생성하지 않고 class를 이용해서 직접 호출할 수 있는 메서드로, 클래스 속성의 초기화에 주로 사용한다. (인스턴스 상태를 변화시키지 않는 메서드를 만들 때도 사용한다고 함)
@staticmethod 데코레이터로 장식하고 self 매개변수 없이 바로 정의Class Method
static method와 유사하지만 첫 번째 매개변수로 클래스 자신을 가리키는 class 인스턴스가 전달된다. (관습적으로 cls라고 명명함)
@classmethod 데코레이터로 장식함파이썬에서는 @로 시작하는 단어를 decorator라고 한다.
함수를 만들 때 특별한 코드를 삽입해서 기능을 추가할 때 사용
class Student :
@staticmethod
def smethod() :
print('static method')
@classmethod
def cmethod(cls) :
print('class method')
print(cls)
Student.smethod()
# >> static method
Student.cmethod()
# >> class method
# >> <class '__main__.Student'>
stu = Student()
stu.smethod()
# >> static method
인스턴스를 통해 static method를 호출하지 말라고 하는 이유?
파이썬의 메모리 구조는 위 그림과 같이 인스턴스 참조를 stack 영역에 저장하고, 인스턴스는 heap의 반영구적인 영역, 실제 클래스는 heap의 영구적인 영역에 저장한다. 그래서 클래스를 통해 static method를 호출하면(Studetn.smethod()) 바로 접근할 수 있는데, 인스턴스를 통해 static method를 호출하면(stu.smethod()) stu가 위치한 stack 영역을 거치고 student 인스턴스가 위치한 heap의 반영구적인 영역에서 참조하는 클래스의 주소를 가지고 다시 heap의 영구적인 영역으로 가서 접근해야 하기 때문에 우회적인 경로로 접근하게 되는 것이다.
이외에도 인스턴스.메서드() 형태로 접근하면 개발자의 의도와는 달리 인스턴스 메서드처럼 보일 수 있기 때문에 코드의 의미 전달 측면에서도 권장되지 않는다.
인스턴스가 필요없는 메서드들은 static method나 classmethod로 정의한다.
파이썬의 인스턴스는 클래스에 정의된 속성 말고도 필요한 경우 새로운 속성을 추가할 수 있다. 그래서 동일한 클래스로부터 만들어진 인스턴스라도 속성이 다르게 만들어질 수 있다. 이렇게 되면 클래스로부터 인스턴스를 생성하는 의미가 없기(?) 때문에 __slots__ 속성으로 제한할 수 있다.
class Student:
def __init__(self):
pass
stu = Student()
stu.num = 1
stu.name = '철수'
stu2 = Student()
stu2.phone = '010-1234-5678'
stu2.nickname = '철철'
# 이 경우에는 에러 X
# --------------
class Student:
# 특정한 속성만 인스턴스가 소유하도록 할 때 사용하는 속성
__slots__ = ['num', 'name']
def __init__(self):
pass
stu = Student()
stu.num = 1
stu.name = '철수'
stu2 = Student()
stu2.phone = '010-1234-5678'
stu2.nickname = '철철'
# >> AttributeError: 'Student' object has no attribute 'phone' and no __dict__ for setting new attributes
private 멤버는 class 내부에서는 접근이 가능하지만 class 외부(인스턴스)에서는 접근이 되지 않는 멤버
class Student :
def __init__(self, name='noname', age=0):
self.name = name
self.__age = age # private 멤버
student1 = Student()
print(student1.name)
# >> noname
print(student1.__age)
# AttributeError: 'Student' object has no attribute '__age'
*public 멤버는 class 외부에서 접근이 가능한 멤버 => 파이썬의 모든 멤버는 기본적으로 public
변수를 호출하는 것처럼 사용하지만 실제로는 getter와 setter 메소드를 호출하는 것으로 처리되는 속성
변수명 = property(fget=None, fset=None, fdel=None, doc=None)
class Student :
def __init__(self, name='noname', age=0):
self.__name = name
self.__age = age
def setName(self, name) :
print("setter 호출!")
self.__name = name
def getName(self) :
print("getter 호출")
return self.__name
name = property(fget=getName, fset=setName)
student1 = Student()
student1.name = '철수' # 변수 호출이 아닌 Accessor를 호출한 것
print(student1.naame)
# >> setter 호출!
# >> getter 호출!
# >> 철수
하위 클래스가 상위 클래스의 모든 것을 물려받는 것
용어
목적
상위 클래스에 존재하는 메서드를 하위 클래스에서 재정의해서 사용하는 것
하위 클래스에서 상위 클래스의 메소드를 호출할 때
super().메소드이름()susper(앞의 클래스이름, self).메소드이름()여러 개의 클래스로부터 상속받는 것으로, 부모 클래스에 동일한 메소드나 속성이 있을 때는 왼쪽에서부터 우선권을 부여한다.
class Base1:
def method(self) :
print("Base1의 메서드")
class Base2:
def method(self) :
print("Base2의 메서드")
class Derived(Base1, Base2) :
def method(self) :
# Base1의 메서드 호출
super().method()
# Base2를 부르고 싶으면 Base2 앞에 상속된 클래스(Base1)를 적어야 함
super(Base1, self).method()
instance = Derived()
instance.method()
# >> Base1의 메서드
# >> Base2의 메서드
자신의 인스턴스를 생성할 수 없는 클래스로, 상속을 통해 하위 클래스에서 필요한 내용을 구현해서 사용한다.
생성법
abc Module을 가져온 후 클래스의 괄호 안에 metaclass=ABCMeta 지정
내용이 없는 메서드로, 추상 클래스에 존재해야 하고 하위 클래스에서 반드시 구현해서 사용해야 한다.
@abstractmethod 데코레이터를 붙여서 추상 메서드로 지정함사용하는 이유
템플릿 메서드 패턴을 구현하기 위해, Polymorphism 구현을 위해
🤔 템플릿 메서드 패턴?
여러 클래스에서 공통으로 사용하는 메서드를 템플릿화 하여 상위 클래스에서 정의하고, 하위 클래스마다 세부 동작을 다르게 구현하는 패턴.
변하지 않는 기능(템플릿)은 상위 클래스에 만들어두고, 자주 변경되며 확장할 기능은 하위 클래스에서 만들도록 하여, 상위의 메소드 실행 동작 순서는 고정하면서 세부 실행 내용은 다양화 될 수 있는 경우에 사용한다.
class Starcraft(metaclass=abc.ABCMeta):
@abc.abstractclassmethod
def attack(self) :
pass
class Protoss(Starcraft) :
pass
star = Protoss() # Error! : 추상 메서드를 구현하지 않았기 때문
# TypeError: Can't instantiate abstract class Protoss without an implementation for abstract method 'attack'
# --------------
class Protoss(Starcraft) :
def attack(self) :
print("Protoss의 공격")
star = Protoss()
star.attack()
# Protoss의 공격
독자적인 기능을 갖는 구성 요소
사용법
import 파일명 : 파일명으로 모듈의 내용을 가져와서 파일명.멤버 형태로 사용
from 파일명 import 속성명 : 파일에서 속성만 현재 모듈로 가져와서 사용
from 파일명 import * : 파일에 있는 모든 속성을 현재 모듈에 포함시켜 사용 (권장X)
import 파일명 as 새로운이름 : 파일의 이름을 새로운 이름을 통해서 접근
ex import numpy as npfrom 파일명 import 속성명 as 새로운이름 : 속성을 새로운 이름으로 사용
모듈을 모아놓은 디렉토리
from 패키지이름 import 모듈이름 형태로 특정 모듈을 가져옴