Python

05.OOP(2)

seunghyeoniya 2023. 8. 17. 08:50

-------------------------

05.OOP

-------------------------

** OOP

1. python에서 클래스 사용

=> Class : 사용자 정의 자료형

 

=> Instance : Class를 기반으로 생성된 객체

 

=> method : Class 내부에 선언된 함수로 Class나 Instance를 가지고 호출

- unbound 호출 : Class로 호출

- bound 호출 : Instance로 호출

 

=> Attribute : Class나 Instance에 사용하는 데이터

Class Attribute : 모든 Instance가 공유하기 위한 속성 - 메서드 외부에 생성

Instance Attribute : Instance 각각이 소유하기 위한 속성 - 메서드 내부에서 self 와 함께 생성

 

=> Accessor(접근자 메서드) : 속성의 값을 사용하기 위해서 리턴하거나 설정하는 메서드

 

// 5.OOP(1)의 내용 간단하게 복습

 

1) 실습

=> 5.OOP(1) 복습

# 클래스 생성

class Student:

def disp(self):

print("인스턴스 메서드")

# 만들어지기는 클래스에 만들어졌지만 인스턴스 없이는 호출할 수 없는 메서드

# setter - 속성을 수정하거나 생성하는 메서드

def setName(self, name):

self.name = name # name이라는 속성이 없으면 만들어서 대입하고 존재하면 수정

# getter - 속성의 값을 사용할 수 있도록 리턴하는 메서드

def getName(self):

return self.name

 

stu1 = Student() # 인스턴스 생성

# 메서드 호출

Student.disp(stu1) # 클래스가 인스턴스의 메서드를 호출 - unbound 호출

stu1.disp() # self에 인스턴스가 대입되어서 메서드를 호출 - bound 호출

 

stu1.setName("seunghyeon") # 입력(출력하려고 기입) : set 사용

print(stu1.getName()) # 출력(입력받아서 사용) : get 사용

 

2) 특수 목적 속성

=> 특별한 기능을 제공하기 위해서 파이썬에서 제공하는 속성

=> "__"으로 시작하고 "__"으로 끝나는 속성

=> 대표적인 속성

__doc__ : 함수를 설명하기 위한 속성

__name__ : 함수의 이름

 

3) 생성자와 소멸자

=> constructor(생성자)

인스턴스를 생성할 때 호출하는 특별한 메서드

직접 생성하지 않아도 메모리 할당을 하고, 참조를 리턴하는 매개변수로 self 만 가지는 생성자가 제공됩니다.

직접 생성자를 만들면 제공되는 생성자는 소멸됩니다.

생성자를 만드는 이유는 처음부터 속성을 만들거나 속성을 원하는 값으로 초기화하기 위함입니다.

파이썬에서는 생성자를 만들 때 __init__이라는 이름을 사용

 

=> destructor(소멸자)

인스턴스가 소멸될 때 호출되는 메서드

이름은 __del__

파이썬에서는 소멸자를 직접 만드는 경우, 매개변수를 self 이외의 것은 할 수 없다.

소멸자를 만드는 경우는 외부 자원의 정리를 직접하고자 하는 경우입니다.

 

파일 핸들링, 네트워크, 데이터베이스를 할 때 '정리'와 '예외처리'가 필수이다.

 

=> 실습을 통하여 입/출력의 위치를 바꾸었을 때 에러가 발생하는 것을 확인하고,

생성자를 통해서 에러가 발생하지 않도록 해결해보겠다.

class Student:

def disp(self):

print("인스턴스 메서드")

# setter - 속성을 수정하거나 생성하는 메서드

def setName(self, name):

self.name = name

# getter - 속성의 값을 사용할 수 있도록 리턴하는 메서드

def getName(self):

return self.name

 

stu1 = Student()

# 메서드 호출

Student.disp(stu1)

stu1.disp()

 

print(stu1.getName()) # 출력(입력받아서 사용) : get 사용

stu1.setName("seunghyeon") # 입력(출력하려고 기입) : set 사용

=> 위의 코드는 에러

 

 

class Student:

# 생성자 - 인스턴스를 생성할 때 호출하는 메서드

# 이 메서드에서 속성을 생성해서 속성을 처음부터 소유하도록 만들어주는 것이 좋습니다.

# 매개변수를 이용해서 초기화하면 만들어질 때 다양한 값으로 초기화가 가능하다.

# 매개변수를 이용해서 초기화할 때 매개변수에 기본값을 설정하지 않으면, 인스턴스를 생성할 때 반드시 매개변수에 값을 대입해야 합니다.

def __init__(self, name = "noname"): # "noname"이라는 기본값을 설정하여 값이 없을 때 "noname"을 출력하도록 한다.

print("인스턴스 생성")

# self.name = "기본값" # 특정한 상수로 생성하고자 경우는 생성자 내부에서 직접 설정

self.name = name

def disp(self):

print("인스턴스 메서드")

# setter - 속성을 수정하거나 생성하는 메서드

def setName(self, name):

self.name = name

# getter - 속성의 값을 사용할 수 있도록 리턴하는 메서드

def getName(self):

return self.name

 

stu1 = Student()

# 메서드 호출

Student.disp(stu1)

stu1.disp()

 

print(stu1.getName()) # 출력(입력받아서 사용) : get 사용

stu1.setName("seunghyeon") # 입력(출력하려고 기입) : set 사용

=> 위의 코드는 생성자를 통해서 에러가 발생하지 않음

 

4) 인스턴스 소멸

=> 파이썬에서는 인스턴스가 차지하고 있던 공간을 Garbage Collection이 정리를 해줍니다.

=> 파이썬에서는 Reference Count라는 개념을 이용해서 정리 대상을 결정합니다.

인스턴스를 만들어서 어떤 변수에 참조를 대입하면 Count는 1이 됩니다.

참조를 다른 변수에 대입하면 Count는 1씩 증가

참조하고 있던 변수가 소멸되거나 None을 대입하면 Count가 1씩 감소합니다.

Count가 0이 되면 정리 대상이 됩니다.

=> Reference Count를 확인하는 방법은 sys 모듈의 getrefcount 라는 함수에 인스턴스를 대입하면 됩니다.

이때 1이 증가하게 나오는데 이것은 getrefcount 자체가 1번 참조하기 때문입니다.

class Student:

def __del__(self):

print("인스턴스 소멸")

 

stu1 = Student() # 인스턴스가 생성되고 참조 카운트가 1이 됩니다.

stu1 = None # 참조를 가리키는 변수에 None을 대입하면 참조 카운트가 1 감소합니다.

# 참조 카운트가 0이면 인스턴스가 소멸됩니다.

 

stu2 = Student() # 참조 카운트 1

stu3 = stu2 # 다른 변수에 참조 카운트를 대입하므로 참조 카운트는 1 증가하여 2

stu2 = None # 참조 카운트가 1 줄어들어도 1 - 인스턴스는 소멸되지 않습니다.

print("프로그램 종료")

-> 인스턴스가 소멸될 때 소멸자 메서드가 호출되는데, 인스턴스는 heap의 영역에 할당되어 프로그램 종료시 소멸하게 된다.

그러므로 프로그램을 종료하면서 인스턴스가 소멸되고 곧바로 소멸자 메서드를 호출한다.

 

5) static 메서드와 class 메서드

=> 클래스를 이용해서 호출하는 메서드

=> 인스턴스 생성없이 사용하기 위한 메서드

-> static 메서드나 class 메서드를 생성해서 사용하는 이유는

인스턴스를 생성하여 메모리를 차지하는 문제를 해결할 수 있다.

 

=> static 메서드

@staticmethod라는 decorator를 이용해서 생성

self가 필요없습니다.

인스턴스가 소유하는 속성에는 접근을 못함

 

=> class메서드

@classmethod라는 decorator를 이용해서 생성

self가 필요없지만 class 인스턴스를 대입받기 위한 매개변수 1개가 필요합니다.

인스턴스가 소유하는 속성에는 접근을 못함

 

=> 2개의 메서드는 클래스 속성에만 접근이 가능합니다.

 

class Student():

# 클래스 속성 - 클래스에 1개만 생성

auto_increment = 0 # 클래스 속성이다. 인스턴스의 속성이 아니다.

# 클래스 속성과 생성자를 이용한 일련번호

def __init__(self):

Student.auto_increment+=1

# 메서드 안에서 클래스 속성은 아래와 같이 부른다.

self.no = Student.auto_increment # 번호를 새기어 주었다.

print(self.no) # 인스턴스를 생성하면 출력

def __del__(self):

print("인스턴스 소멸")

 

@staticmethod

def method(): # static method는 매개변수가 존재하지 않는다.

print("매개변수가 없는 static method")

Student.method() # static method를 이용하면 인스턴스를 만들 필요가 없다.

 

# class Student():

# auto_increment=0

# @classmethod

# def method(cls):

# Student.auto_increment=100 # 참조 카운트가 100부터 시작한다.

# Student.method()

 

# 번호 새기기

stu1 = Student()

print(stu1.no) # 별도 출력

stu2 = Student()

print(stu2.no) # 별도 출력

 

6) 속성 생성 제한

=> class 안에 존재하는 __slots__에 속성 이름을 list로 나열하면, 이 속성들 이외에는 생성할 수 없습니다.

class Student:

# name 과 age 속성만 사용하도록 제한해보겠습니다.

__slots__ = ["name", "age"]

 

stu1 = Student()

stu1.name = "seunghyeon"

stu1.age = 24

# stu1.job = "Programing Beginner" # 위에서 제한을 걸었기 때문에 job은 생성할 수 없다고 에러가 발생한다.

 

7) 인스턴스가 접근할 수 없는 속성 - private

=> 속성 이름을 결정할 때 __를 붙이면 인스턴스가 접근할 수 없는 속성이 만들어 집니다.

class Student:

def __init__(self):

self.name = "seunghyeon"

self.__no = 1 # 속성의 이름이 "__"로 시작하면 인스턴스가 속성에 직접 접근할 수 없게 된다. 메서드 내에서만 사용해야 한다.

 

stu1 = Student()

print(stu1.name)

# print(stu1.__no) # 위와 같은 이유로 "__no"라는 속성에 접근이 불가능하다.

 

8) property

=> 속성을 조작하는 메서드를 어떤 이름에 연결해두어 속성을 조작하는 것처럼 보이지만 실제로는 메서드를 호출하도록 만드는 것

=> 프로퍼티이름 = property(fget=None, fset=None, fdel=Nonoe, doc=None)

class Student:

def __init__(self, name="noname"):

self.__name = name # 속성 이름이 "__"로 시작하므로 인스턴스를 이용하여 접근 불가

# 접근자 메서드

def getName(self):

print("name의 getter 호출")

return self.__name

def setName(self, name):

print("name의 setter 호출")

self.__name = name

# 프로퍼티 생성

# name을 호출하면 getName 메서드가 호출되고 name에 값을 대입하면 setName 메서드가 호출됩니다.

name = property(fget=getName, fset=setName)

 

stu = Student()

# property를 이용한 getter과 settter 호출

stu.name = "Analyst"

print(stu.name)

# name이라는 속성을 이용하는 것처럼 보이지만, property를 통해 메서드를 호출하는 것이다.

 

stu = Student()

# setter와 getter를 직접 호출

stu.setName("Developer")

print(stu.getName())

 

=> decorator를 이용해서도 생성 가능

class Student:

def __init__(self, name="noname"):

self.__name = name # 속성 이름이 "__"로 시작하므로 인스턴스를 이용하여 접근 불가

# 접근자 메서드

@property # getter 설정

def name(self):

print("name의 getter 호출")

return self.__name

@name.setter

def name(self, name):

print("name의 setter 호출")

self.__name = name

 

stu = Student()

stu.name = "Analyst"

print(stu.name)

 

9) operator overloading(연산자 오버로딩)

=> 제공되는 연산자의 기능을 변경해서 사용하는 것

=> 파이썬에서는 각 연산자에 오버로딩이 가능한 메서드를 제공

메서드 이름은 __이름__입니다.

+는 __add__

==는 __eq__ -> (== : 값(내부요소)를 확인, 위치 비교는 is 이다.)

=>연산자 오버로딩을 할 때 매개변수의 개수는 변경이 불가능합니다.

class Student:

def __init__(self, name="noname"):

self.name = name

 

# + 연산자 오버로딩

def __add__(self, other):

return self.name + other.name # name을 합친다.

# == 연산자 오버로딩

def __eq__(self, other):

return self.name == other.name # name(데이터)를 비교한다. (id를 확인하지 않는다.)

 

stu1 = Student("seunghyeon")

stu2 = Student("seunghyeon")

 

print(stu1 + stu2) # stu1과 stu2 모두 seunghyeon이라는 데이터를 공통적으로 갖지만 할당 받은 id는 다르다.

print(stu1 == stu2) # stu1과 stu2의 데이터가 같으므로 True를 반환한다. 내부의 값을 확인할 때는 == 연산자 오버로딩을 사용한다.

print(stu1 is stu2) # stu1과 stu2의 id가 다르기 때문에 False를 반환한다. id가 같은지 확인할 때는 is를 사용한다.

 

 

class Student:

def __init__(self, name="noname", count=0):

self.name = name

self.count = count

def __add__(self, other):

return self.count + other.count

 

stu1 = Student("바나나", 3)

stu2 = Student("딸기", 2)

print(stu1 + stu2) # "__add__"를 이용하여 인스턴스끼리 count 속성을 가지고 덧셈을 가능하게 하였다.

 

#python

'Python' 카테고리의 다른 글

05.OOP(1)  (0) 2023.08.17
04.Function(2)  (0) 2023.08.16
04.Function(1)  (0) 2023.08.05
03.Control Statement  (0) 2023.08.05
02.Variable_Operator  (0) 2023.08.05