파이썬3 노트

모듈/함수 공부: [데이터 모델] ①

Jonchann 2018. 6. 15. 15:53

벡터 클래스를 만들면서 일단 알려줬으니까 __add__, __sub__ 라는 형식을 계속 쓰긴 했지만 뭔지는 모르는 상태로 썼다.

그래서 제대로 찾아봤다.

일단 __어쩌구__ 라는 식으로 언더 바 2개로 메소드를 묶는 이유는 다른 파일에서 import로 메소드를 호출하게끔 할 수 있도록 해당 파일에서 생성한다는 표식인 것 같다. 일단 내 이해로는 그렇다.

간단히 말하자면: __repr__(self)를 Vector라는 class 안에 생성해 놓으면 다른 파일에서 import Vector를 해서 repr(객체)라는 식으로 사용할 수 있는 말인 것 같다. 그리고 산수 연산자의 경우, 예를 들어, __add__의 경우 class 밖에서 +라는 연산자 기호를 사용해서 계산을 할 수 있게 된다.


(혹시 틀렸다면 댓글로 알려주세요!!)



[데이터 모델]


파이썬은 객체지향형 프로그래밍 언어라고 하는데 여기서 객체란 파이썬이 데이터를 추상화한 것이다. 라고 한다.

모든 객체는 아이덴티티, type, value를 가지며 객체의 아이덴티티(대체 뭐람)는 한 번 만들어진 후에는 변경되지 않으니 메모리 상에서의 객체의 주소로 생각하면 될 듯 하다. 고 한다. 이에 대한 설명은 너무 방대해서 전체적으로 다 보진 않고 당장 필요한 것만 적을 생각이다.

발등에 불 떨어지면 다시 찾아봐야지: 데이터 모델


아래 코드는 행렬, 벡터 연산을 하는 코든데 클래스의 개념을 이해하기 위한 연습문제로 선배에게 받았다.

class Matrix(object):
    def __init__(self, data: List[List[float]]):
        self.data = data

    def __len__(self) -> int:
        raise NotImplementedError

    def __add__(self, other: 'Matrix') -> 'Matrix':
        raise NotImplementedError

    def __sub__(self, other: 'Matrix') -> 'Matrix':
        raise NotImplementedError

    def __mul__(self, other: 'Matrix') -> 'Matrix':
        raise NotImplementedError

    def __matmul__(self, other: 'Matrix') -> 'Matrix':
        raise NotImplementedError

    def __repr__(self) -> str:
        raise NotImplementedError

__연산자__  메소드는 클래스 내에서 이런 식으로 쓰면 되는 듯.



1. __new__


아래는 100本ノック 풀면서 적어놓은 코드이다.

__new__는 클래스의 새 인스턴스를 위해 호출되는 메서드라고 하는데 그냥 이해하지 않고 외워서 쓰고 있다: 함수를 만들어놓고 실행시키기 위한 역할이라는 것은 인식하고 있다.

이 때 세트로 쓰이는 것이 if __name__ == '__main__': 이라는 문구.

finame = 'jawiki-country.json.gz'

def extract_country(country_name):
	with gzip.open(finame, 'r') as gzip_file:
		for line in gzip_file:
			article = json.loads(line)
			if article['title'] == country_name:
				return article
	return

if __name__ == '__main__':
	article = extract_country("イギリス")
	for text in article:
		print(article["text"])
	print('\n\n------------extraction-------------\n\n')

파이썬은 함수가 자동으로 실행되지 않기 때문에 if __name__ == '__main__':로 위에 만들어놓은 함수를 실행시켜야 한다고 한다.

if 문으로 실행할 함수와 같은 파일에 있을 때만 참이 되고 다른 파일에 import된 경우에는 if문은 거짓이 되어 실행되지 않는다.



2. __init__


__init__ 메소드는 앞서 적은 __new__로 만들어진 인스턴스를 호출할 떼 쓰인다.

가장 위에 있는 코드를 완성해 적은 후 

a = Matrix(...)
b = Matrix(...)

라는 식으로 a, b라는 행렬을 만들어주면 __init__이 받아 아래 있는 함수를 적용시키기 위해 저장하는 역할이라고 이해하고 있다.



3. __add__(self, other)


여기서부터는 산수를 위한 메소드이다.

말 그대로 더하기 연산을 위한 것으로 self.data와 other.data를 더해준다.

그냥 보면 간단하게 쓸 수 있어 보이지만 아래 코드를 보고 난 멘붕 옴.

def __add__(self, other: Union[float, 'Vector']) -> Union[float, 'Vector']:
    if isinstance(other, float):
        return Vector([element + other for element in self.data])
    elif isinstance(other, Vector):
        return Vector([i + j for i, j in zip(self.data, other.data)])
    else:
        raise TypeError(f'unsupported type :: {type(other)}')

먼저 other: Union[float, 'Vector']와 Union[float, 'Vector']: 라는 것은 저번 글에 나오는 typing hint로 파이썬에게 other의 정체를 알려주는 역할을 한다.

즉, Union[float, 'Vector']의 Union이라는 것은 합집합을 의미하기 때문에 여기서는 other가 float일 수도 있고 Vector일 수도 있다고 파이썬에게 알려주고 있는 것이다.


둘째로 if isinstance(other, float): 라는 것은 other가 float라면 True라는 뜻. 따라서 elif isinstance(other, Vector):도 마찬가지로 other가 Vector라면 이라는 뜻이다.



4. __radd__(self, other):


위에 있는 연습 코드에는 __radd__ 메소드는 없지만 다른 코드에서 발견하고  뭐지 싶어 가져왔다.

def __radd__(self, other):
    return self.__add__(other)

__add__를 리턴하고 있다.

어차피 똑같은 것 아닌가 하고 찾아보니 만약에

class Add(object):
    def __init__(self, data):
        self.data = data

    def __add__(self, data):
        self.data += data

 라는 클래스와 함수가 정의되어 있다고 했을 때

d = Add(50)
d + 100

라는 값과 식을 준다면 제대로 150이라는 값이 나오지만  100+d는 에러가 난다.

이렇게 순서가 바뀌는 경우를 위해 메소드 이름 앞에 r을 붙인 메소드가 존재한다고 한다.

__radd__는 바로 그 예인 것.



5. __len__(self)


내장되어있는 len()함수와 같은 역할을 만들어주기 위해 불러온 메소드다. __len__(self)는 시퀀스형 연산자인데 '시퀀스형 연산자'라는 것은 컨테이너 객체를 구현하기 위한 메서드라고 한다. 시퀀스라는 것은 리트시퀀스형 연산자에는 __contain__(self, item), __getitem__(self, key), __setitem__(self, key, value), __delitem__(self, key) 등이 있다고 한다. 

__len__이라는 건 그냥 이름이고 안에다가 len()으로 행렬이나 벡터의 길이를 재 줘야 한다.


 

6. __sub__(self, other)


self.data - other.data를 해주는 빼기 메소드



7. __mul__(self, other)


self.data 와 other.data를 곱해주는 메소드. 그냥 x, y가 주어졌을 때 이 둘의 곱을 구하는 것은 간단하지만 행렬이나 내적이라면 복잡해진다(내기준).

아래 코드는 선배가 numpy를 대신해서 만든 파일 속 벡터 연산 중 하나를 가져온 것이다.


if문: 만약 other가 float라면 self.data(벡터1) 속 element 에 float를 곱해라

elif문: 만약 other가 벡터라면 self.data(벡터1), other.data(벡터2) 속 i 에 j 를 곱해라

else문: 둘 다 아니라면 패스(TypeError를 막는 명령어라고 이해하고 있음)

class Vector(object):
    def __init__(self, data: List[float]):
        self.data = data

    def __mul__(self, other: Union[float, 'Vector']) -> Union[float, 'Vector']:
        if isinstance(other, float):
            return Vector([element * other for element in self.data])
        elif isinstance(other, Vector):
            return Vector([i * j for i, j in zip(self.data, other.data)])
        else:
            raise TypeError(f'unsupported type :: {type(other)}')


아래는 같은 파일 행렬 class에서 곱셈 연산만 가져왔다.

벡터와 다르게 행렬은 열을 고려해야할 필요가 있다.


if문: other가 float라면 self.data(행렬1)의 row의 element 에 float를 곱해서 결과는 행렬로 출력하라

else문: other가 행렬이라면 self.data(행렬1), other.data(행렬2) 의 열(행렬1)과 열(행렬2)의 원소(x, y)끼리 곱해서 결과는 행렬로 출력하라

class Matrix(object):
    def __init__(self, data: List[List[float]]):
        self.data = data

    def __mul__(self, other: Union[float, 'Matrix']) -> 'Matrix':
        if isinstance(other, float):
            return Matrix([
                [element * other for element in row]
                for row in self.data
            ])
        else:
            return Matrix([
                [x * y for x, y in zip(row1, row2)]
                for row1, row2 in zip(self.data, other.data)
            ])



8. __matmul__(self, other)


이 메소드는 내적 메소드이다.

먼저 위와 같은 벡터 클래스에서 내적 코드를 가져왔다.


if문: other가 벡터라면 self.data(벡터1), other.data(벡터2)의 i 에 j 를 곱해라

else문: 아니라면 에러가 날 것이므로  raise TypeError로 방지

    def __matmul__(self, other: 'Vector') -> float:
        if isinstance(other, Vector):
            return sum(i * j for i, j in zip(self.data, other.data))
        else:
            raise TypeError(f'unsupported type :: {type(other)}')


이번엔 위와 똑같은 행렬 클래스에서 내적 코드를 가져왔다.


if문: other가 벡터라면 self.data(벡터1) 의 열과 other(벡터2) 를 @(내적)을 구해서 결과를 벡터로 출력하라

else문: self.data(벡터1) 의 열(가로; 많은 열 중 하나)의 원소 x와 other.data(벡터2) 의 행(세로; 많은 행 중 하나)의 원소를 곱해서 더한 후 그 결과를 행렬로 출력하라 

    def __matmul__(self, other: Union['Vector', 'Matrix']) -> Union['Vector', 'Matrix']:
        if isinstance(other, Vector):
            return Vector([Vector(row) @ other for row in self.data])
        else:
            return Matrix([
                [sum(x * y for x, y in zip(row, col)) for col in zip(*other.data)]
                for row in self.data
            ])


9. __repr__(self)


repr이라는 것은 representation의 축약어로 행렬이나 벡터를 프로그램이 그 형태로 출력할 수 있게 도와주는 메소드이다.




'파이썬3 노트' 카테고리의 다른 글

numpy 공부: [선형대수]  (0) 2018.08.10
numpy 공부: [Indexing] (3)  (0) 2018.08.09
numpy 공부: [Indexing] (2)  (0) 2018.08.08
numpy 공부: [indexing] (1)  (0) 2018.08.07
모듈/함수 공부: [typing] [@property] [[int, ...]]  (0) 2018.06.13