본문 바로가기

Data_Analysis_Track_33/Python

Python_05-4(특수 메소드, class 변수와 메소드, static 메소드)

특수 메소드 : 특정한 상황에서 사용될 때 자동으로 호출되도록 파이썬 실행환경에 정의된 약속된 메소드들이다. 

- 객체에 특정 기능들을 추가할 때 사용한다.
- 언제 호출되는지 정해져 있다.(그렇기 때문에 언제 호출되는지를 아는 것이 중요) ex) __init__() => 객체 생성할 때 호출 된다.
형식 : 메소드 명이 더블 언더스코어로 시작하고 끝난다. ->__이름__()   ex) __init__(), __str__() 
실행환경이 실행시켜주는 메소드, 콜백 메소드(call back) 또는 매직 메소드(Magic Method), 던더(DUNDER) 메소드라고도 한다.

주요 특수 메소드

__init__(self [, …])
- Initializer
- 객체 생성시 호출 된다.
- 객체 생성시 Attribute의 값들을 초기화하는 것을 구현한다.
- self 변수로 받은 instance에 Attribute를 설정한다.

__call__(self [, …])
- 객체를 함수처럼 호출 하면 실행되는 메소드
- Argument를 받을 Parameter 변수는 self 변수 다음에 필요한대로 선언한다.
- 처리결과를 반환하도록 구현할 경우 return value 구문을 넣는다. (필수는 아니다.)

class Plus:
    def __init__(self, num1, num2):
        self.num1 = num1
        self.num2 = num2
        
    # def calculate(self):
        # return self.num1 + self.num2
    def __call__(self):
        # 객체를 함수처럼 호출할 때 호출될 메소드.
        # 객체가 하나의 메소드를 제공하거나 메인 메소드가 하나 있을 때 정의한다.
        return self.num1 + self.num2

 

class Square:
    def __init__(self, num):
        self.num = num
        
    # 제곱하는 메소드
    # def calculate(self, n):
    #     return self.num ** n
    def __call__(self, n):
        return self.num ** n

 

square = Square(3) # 3을 파라미터로 받아 self.num은 3이 된다.
# square.calculate(5)
square(5) # 3 ** 5의 연산 결과를 반환한다.

 

plus = Plus(10, 20) # Plus 클래스에 10과 20이 각각 num1, num2가 된다.

plus() # 객체() -> 객체를 함수처럼 호출(실행) -> __call__()가 호출됨
# 사용하는 입장에서 더 편하게 이용할 수 있다.


__repr__(self)
- Instance(객체) 자체를 표현할 수 있는 문자열을 반환한다
- 값을 표현하는 문자열을 return(값 자체를 생성하는 코드를 문자열로 반환)

__str__(self)
- Instance(객체)의 Attribute들을 묶어서 문자열로 반환한다.
- 객체의 값을 알려준다. 단순히 객체의 정보를 알 수 있도록 한다.

# __repr__() : 객체-값 -> 값의 표현식을 문자열로 반환. 30 -> '30', True -> 'True'
# Square 객체(값) - Square(30) -> 'Square(30)'
# Person('홍길동', 20, '서울') -> 'Person('홍길동', 20, '서울')'
# Person('이순신', 10, '인천') -> 'Person('이순신', 10, '인천')'

 

class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address
        
    def __repr__(self):
        return f"Person('{self.name}', {self.age}, '{self.address}')"
    
    def __str__(self):
        return f"이름 : {self.name}, 나이 : {self.age}, 주소 : {self.address}"

 

p = Person('홍길동', 20, '서울')

v = repr(p) # p.__repr__() 호출한 결과(str)을 반환. -> p 값을 만드는(생성하는) 구문을 반환
v

 

v2 = str(p) # str(값) -> 값.__str__() 의 반환값을 반환해 준다.
print(v2)


eval(문자열) : 문자열이 실행가능한 구문인지 파이썬에서 확인후 실행 가능하면 실행한 결과를 반환

print(eval('1 + 1')) # 실행가능한 구문인지 파이썬에서 확인후 실행 가능하면 실행한 결과를 반환
p2 = eval(v)
p2.name, p2.age, p2.address


연산자 오버라이딩 : 객체와 객체의 연산이 되도록 한다.

연산자의 피연산자로 객체를 사용하면 호출되는 메소드들
다항연산자일 경우 가장 왼쪽의 객체에 정의된 메소드가 호출된다.
a + b 일경우 a의 __add__() 가 호출된다.
비교 연산자
__eq__(self, other) : self == other : == 로 객체의 내용을 비교할 때 정의 한다.
__lt__(self, other) : self < other
__gt__(self, other): self > other : min()이나 max()에서 인수로 사용할 경우 정의해야 한다.
__le__(self, other): self <= other
__ge__(self, other): self >= other
__ne__(self, other): self != other

산술 연산자
__add__(self, other): self + other
__sub__(self, other): self - other
__mul__(self, other): self * other
__truediv__(self, other): self / other
__floordiv__(self, other): self // other
__mod__(self, other): self % other

class Person:
    def __init__(self, name, age, address):
        self.name = name
        self.age = age
        self.address = address
        
    def __repr__(self):
        return f"Person('{self.name}', {self.age}, '{self.address}')"
    
    def __str__(self):
        return f"이름 : {self.name}, 나이 : {self.age}, 주소 : {self.address}"
    
    def __eq__(self, other):
        # self == other 연산시 호출됨. -> 속성값이 같으면 True가 나오도록 처리.
        # __eq__() 구현하면 __ne__()까지 구현할 필요는 없다.
        if isinstance(other, Person):
            return self.name == other.name and self.age == other.age and self.address == other.address
        else:
            return False
        
    def __gt__(self, other):
        # self > other 연산시 호출 - 나이 비교
        if isinstance(other, Person):
            return self.age > other.age
        elif isinstance(other, (int, float)): # 나이를 숫자와 비교
            # self > 30, self > 25.4
            return self.age > other
        else:
            # return False
            # 에러(Exception) 발생
            raise TypeError(f"Person 타입과 {type(other)}는 '>' 연산을 할 수 없습니다.")

 

p1 = Person('홍길동', 20, '서울')
p2 = Person('이순신', 30, '인천')
p3 = p1
p4 = Person('홍길동', 20, '서울')
p5 = Person('홍길동', 20, '부산')
print('1', p1 == p2) # 기본 : p1과 p2가 같은 객체인지 연산자 오버라이딩 -> p1.__eq__(p2)
print('2', p1 == p3)
print('3', p1 == p4) # 값은 같지만 다른 객체이다.
print('4', p1.name == p4.name) # 값은 같다.
print('5', p1 == p5)

 

print(p1 > p2)
print(p2 > p1)

print(p1 > 10) # p1의 나이가 10보다 큰지?
print(p1 > 50)
print(p2 > 100.8)


class변수, class메소드
class변수 : 클래스 자체의 데이터, class 블럭에 변수 선언, 정보은닉 가능
- 클래스이름.변수 -> 접근
class메소드 : 클래스 변수를 처리하는 메소드
- @classmethod 데코레이터 붙인다.
- 첫번째 매개변수로 클래스를 받는 변수를 선언한다. 이 변수를 이용해 클래스 변수나 다른 클래스 메소드를 호출 한다.

static 메소드 : 클래스의 메소드로 클래스 변수와 상관없는 단순기능을 정의한다.
- @staticmethod 데코레이터를 붙인다.
- Parameter에 대한 규칙은 없이 필요한 변수들만 선언한다.

 

class Circle:
    __PI = 3.14 # class 블럭 안에 선언 -> 클래스변수 (class variable), 클래스 변수도 정보은닉이 가능하다. -> _Cirlce__PI로 변경된다.
    __version__ = "1.0"
    # PI의 값을 변경(setter), 조회(getter)
    
    @classmethod
    def set_PI(clazz, new_PI): # 첫번째 파라미터 - class 자체를 받는다. 관례로 clazz나 clz라고 한다.
        # clazz -> class 변수, 다른 class 메소드를 호출할 때 사용.
        if new_PI in [3.14, 3.14159]:
            clazz.__PI = new_PI
        else:
            print('변경못함 현재 PI값 : ', clazz.__PI)
            
    @classmethod
    def get_PI(clazz):
        return clazz.__PI
            
    def __init__(self, radius):
        # self -> attribute(instance 변수), 다른 instance method 호출
        self.radius = radius
        
    def calc_area(self): # 원의 너비
        return self.radius * self.radius * Circle.__PI # method에서 class 변수 사용.
    
    @staticmethod
    def class_version():
        # class변수나 instance변수를  사용하지 않는 메소드. -> static method
        # 함수처럼 사용하는 메소드(클래스 소속.)
        return '1.0'

 

print(Circle.__version__)
print(Circle.class_version())

 

Circle.set_PI(3)       # set_PI -> 조건 충족 여부
Circle.set_PI(3.14159)
Circle.set_PI(3.14)
Circle.set_PI(3)

Circle.get_PI()        # get_PI -> 현재 클래스 변수 __PI의 값 조회

c = Circle(5)
print(c.calc_area())   # 원의 너비 구하기 -> 5 * 5 * 3.14

 

Circle.__dict__

# 메소드 없이 class 변수 조회
print(Circle._Circle__PI)

# class 변수 변경
Circle._Circle__PI = 3.14159
print(Circle._Circle__PI)

print(c.calc_area()) # 원의 너비 구하기 -> 5 * 5 * 3.14159