저번의 if __name__ == "__main__": 에 이어, 자주 보이는 다른 구문이 있다.

2024.01.19 - [✍🏻Language & FrameWork/Python] - [Python] if __name__ = "__main__": 이란 ?

 

[Python] if __name__ = "__main__": 이란 ?

Python 소스코드를 보면 module(.py 파일) 내부에 if __name__ == "__main__": 이라는 구문을 많이 볼 수 있다. Python 문법을 알고, 함수도 어느정도 작성할 수 있는 나에게 위의 구문은 보기만해도 낯선 것에

jijibae.tistory.com


class 를 정의하면 바로 첫 method(함수) 가 

    def __init__(self, ~~~) 의 꼴을 띄는 것이다.

 

또 새로운 것에 대한 강한 거부감이 들었지만, 이번에도 하나하나 뜯어보며 이해해보자.


1. Class 란?

"Class 와 Object(객체)" 의 관계는 많은 책에서 "붕어빵 틀과 붕어빵"로 비유한다.

  • Class : 똑같은 무엇인가를 계속 만들어낼 수 있는 설계 도면 같은 것 (붕어빵 틀)
  • Object : Class에 의해서 만들어진 피조물(붕어빵)

Class - Object

# 객체 a 생성
class bread:
    pass
    
a = bread()

 

가끔 객체를 인스턴스로 일컫기도 하는데 이는 객체와 클래스의 관계를 알려주는 것이다.

  • 클래스에 의해서 만들어진 객체를 인스턴스라고도 한다.
  • a를 단독으로 지칭할 때는 'a는 객체'
  • 클래스와 연관지어서 지칭할 때는 'a는 bread의 인스턴스'

2. Class 예시

[Do it] 점프 투 파이썬 에서는 Class를 설명할 때 계산기를 예로 든다.

# class 정의
class FourCal:
    def setdata(self, first, second):
        self.first = first
        self.second = second
        
    def add(self):
        result = self.first + self.second
        return result
        
    def mul(self):
        result = self.first * self.second
        return result
# 예시
a = FourCal()
b = FourCal()

a.setdata(4,2)
b.setdata(3,7)

print(a.add())
print(b.add())

print(a.mul())
print(b.mul())

6

8

10

21

 

정의를 보면 setdata(self, first, second)는 3개의 매개변수를 필요로하는데

실제 setdata에는 2개의 매개변수(first, second) 만을 전달한다.

  • 일반적인 함수와 달리 메서드의 첫번째 매개변수 self는 다른 의미를 가진다.
  • 파이썬 메서드의 첫번째 매개변수 명은 관례적으로 self 라는 이름을 사용하며, 다른 이름을 사용해도 괜찮다
  • 객체와 호출 입력 값들이 메서드에 어떻게 전달되는지에 대한 설명은 아래와 같다.

 

3. __init__(self) 란?

위의 setdata의 정의를 보면 다음과 같다.

def setdata(self, first, second):
    self.first = first
    self.second = second

이 함수의 역할을 보면 객체에 초깃값을 설정한다.

만일 객체에 초깃값을 설정하지 않고, add(), mul() 메서드를 수행하면 오류가 발생한다.

그 이유는 당연한데 해당 객체에 first, second 의 변수 없이 더하기, 곱하기를 수행하는 명령을 주었기 때문이다.

이렇게 메서드를 호출하여 객체의 초깃값을 설정하는 방법도 있지만, 생성자를 구현하는 것이 더 좋은 방법이다.

이때 파이썬 메서드 명을 __init__ 을 사용하면 이 메서드는 생성자가 된다.

def __init__(self, first, second):
    self.first = first
    self.second = second

2) 의 예에서는 setdata(4,2) 와 같이 객체에 초깃값을 설정하기 위해 따로 변수 값을 설정하는 메소드를 호출했다.

반면, __init__ 을 사용하여 생성자를 구현하면 자동으로 객체에 초깃값을 설정해준다. (가장 중요한 부분)

 

 

4. 생성자(Conductor)는 무엇일까?

  • 객체가 생성될 때 '자동으로 호출'되는 메서드를 의미한다.
  • 생성자를 사용하여 객체를 생성할 때에는 선언시 초기값을 함께 전달해야 한다.

 

5. 2)의 코드를 수정

class Fourcal():
    def __init__(self, first, second):
        self.first = first
        self.second = second
        
    def add(self):
        result = self.first + self.second
        return result
        
    def mul(self):
        result = self.first * self.second
        return result
a = FourCal(4, 2)
b = FourCal(3, 7)

# setdata() 메소드 생략

print(a.add())
print(a.mul())
print(b.add())
print(b.mul())

6

8

10

21

 

6. 마치며

마지막으로 __init__ 메서드의 매개변수에 어떠한 값들이 대입되는지 정리하며 마치겠다.

매개변수
self 생성되는 객체
first 4
second 2

 

Python 소스코드를 보면 module(.py 파일) 내부에

if __name__ == "__main__": 이라는 구문을 많이 볼 수 있다.

 

Python 문법을 알고, 함수도 어느정도 작성할 수 있는 나에게 위의 구문은 보기만해도

낯선 것에 의한 강한 거부감이 든다...

 

하지만 프로젝트를 진행하는 과정에서 이 문법을 포함한 몇 가지의 문법들은 몰라서는 소스코드를 한참을 봐도 이해가 안되고,

사용할 때 마다 기술 부채를 느끼기 때문에 이번 기회에 제대로 뜯어보며 정리하려고 한다.

딱 나와 같은 수준을 가진 Python 사용자가 있다면 이 글이 도움이 되었으면 좋겠다.


 

< 가정 >

1. test.py 와 exam.py 이 있음

2. exam.py 에서 test 를 import 한다. (import test)

 

1. __name__ ,  __main__  이란?

__name__ : 모듈의 이름을 담고 있는 파이썬 내장 변수, test.py 라는 파일에서 확장자 .py를 제외한 test가 모듈의 이름

__main__ : 최상위 코드가 실행되는 환경의 이름. 실행을 시작하는 첫 번째 Python 모듈

 

__main__ 의 이해가 어려울 듯 하여 다음의 예시를 참고

 

1-1) 간단한 예

# test.py 에서 다음을 작성 후 test.py 에서 실행
print('__name__ value :', __name__)

__name__ value : __main__     

(import 없이 test.py 에서 실행을 했기 때문에 최상위 코드에서 실행한 것 __name__ = __main__)

# 위의 test.py 수정없이 작성
# exam.py 에서 다음을 작성 후 exam.py 에서 실행
import test
print('__name__ value :', __name__)

__name__ value : test

__name__ value : __main__

(import test 를 했으므로 test.py 에 있는 print() 가 실행되는데 이때 exam.py 입장에서 모듈의 이름은 test 이다.

이후 exam.py 에 있는 print() 가 실행되는데, exam.py 입장에서 모듈의 이름은 __main__ 이다. (최상위 코드 환경, exam 아님 주의

따라서 exam.py 에서 실행을 하면 test.py 모듈을 가져온 것에서 1번 실행, exam.py 에서 1번 실행 하여 총 2회 실행된다.

 

2. 왜 사용하는가?

위의 예에서 exam.py 에서 test를 import 할 때, test.py 에 있는 명령어가 실행되는 것을 보았다.

만일, test.py 에 있는 명령어가 실행되는 것을 원치 않을 경우, if __name__ == "__main__": 을 추가하여 막을 수 있다.

 

 

3. 모든 경우의 예시

필자는 스스로 다음 코드를 돌려보면서 과정을 이해를 했다.

<가정>에서 나올 수 있는 경우는 총 4개이다.

작성의 편의를 위해 if __name__ == "__main__": 를 A 라고 치환하면 아래의 4경우가 존재한다.

(모든 실행은 exam.py 에서 진행된다.)

  1. test.py + exam.py  모두  A 가 없는 경우
  2. test.py 에는 A 가 있지만, exam.py 에는 A 가 없는 경우
  3. test.py 에는 A 가 없고, exam.py 에는 A 가 있는 경우
  4. test.py + exam.py 모두 A 가 있는 경우

3-1) test.py + exam.py  모두  A 가 없는 경우

# test.py 에서 작성
print('__name__ value :', __name__)
# exam.py 에서 작성
import test
print('__name__ value :', __name__)

__name__ value : test

__name__ value : __main__

 

3-2) test.py 에는 A 가 있지만, exam.py 에는 A 가 없는 경우

# test.py 에서 작성
if __name__ == "__main__":
    print('__name__ value :', __name__)
# exam.py 에서 작성
import test
print('__name__ value :', __name__)

__name__ value : __main__   (test 가 모듈이름 이므로 첫 if 문 실행 X)

 

3-3) test.py 에는 A 가 없고, exam.py 에는 A 가 있는 경우

# test.py 에서 작성
print('__name__ value :', __name__)
# exam.py 에서 작성
import test
if __name__ == "__main__":
    print('__name__ value :', __name__)

__name__ value : test

__name__ value : __main__

(3-1 과 동일)

 

3-4) test.py + exam.py 모두 A 가 있는 경우

# test.py 에서 작성
if __name__ == "__main__":
    print('__name__ value :', __name__)
# exam.py 에서 작성
import test
if __name__ == "__main__":
    print('__name__ value :', __name__)

__name__ value : __main__

(3-2 와 동일)

 

 

4. 마치며

3)의 결과로부터

사실상 작성하는 모듈이 import 될 여지가 없다면 if __name__ == "__main__": 구문은 필요없다.

그 이유를 답할 수 있다면 이 process를 완벽하게 숙지 했다고 생각할 수 있다.

이제 우리는 module 단위로 함수를 작성하는 등. 효율적인 Python 프로그래밍을 할 수 있게 되었다. 복잡한 프로젝트에 도전해보자.

잘못된 정보가 있으면 피드백 주세요 감사히 수정하겠습니다.

coefficient_matrix = [[2, 2, 1], [2, -1, 2], [1, -1, 2]]
constant_vector = [9, 6, 5]

위와 같이 Matrix와 Vector를 List 형식으로 표현할 수 있을 것이다.

하지만 다음과 같은 문제가 발생한다.

 

  • 다양한 Matrix 계산을 어떻게 만들 것 인가?
  • 굉장히 큰 Matrix 를 표현하는 방법
  • 처리 속도 문제 - 파이썬은 Interpreter 언어

적절한 패키지를 사용하자


Numpy

  • Numerical Python
  • 파이썬의 고성능 과학 계산용 패키지
  • Matirx와 Vector와 같은 Array 연산의 사실상 파이썬의 표준

 

Numpy 특징

  • 일반 List에 비해 빠르고, 메모리 효율적
  • 반복문 없이 데이터 배열에 대한 처리를 지원함
  • 선형대수와 관련된 다양한 기능을 제공함
  • C, C++, Fortran 등의 언어와 통합 가능

Numpy 와 Python 데이터를 저장하는 차이

 

1. Array 생성

  • numpy는 np.array 함수를 활용하여 배열을 생성 -> ndarray
  • numpy는 하나의 데이터 type만 배열에 넣을 수 있음
  • List와 가장 큰 차이점으로 Dynamic typing not supported
  • C의 Array를 사용하여 배열을 생성함
import numpy as np

test_array = np.array([1, 4, 5, 8], float)    # 데이터 타입 지정
print(test_array)
print(type(test_array[3]))

[1. 4. 5. 8.]

numpy.float64

 

2. Array Shape

  • shape : numpy array의 dimension 구성을 반환함
  • dtype : numpy array의 데이터 type을 반환함
  • ndim : numer of dimension
  • size : data의 개수
import numpy as np

test_array = np.array([1,4,5,"8"], float)  #Sring Type의 데이터를 입력해도
print(test_array)
print(type(test_array[3]))  # Float Type으로 자동 형변환 실시
print(test_array.dtype)     # Array(배열) 전체의 데이터 Type을 반환함
print(test_array.shape)     # Array(배열) 의 shape을 반환함

print(np.array(test_array, int).ndim)    # numer of dimension
print(np.array(test_array, int).size)    # 총 data의 개수

[1. 4. 5. 8.]
<class 'numpy.float64'>
float64
(4,)     <-  Tuple 타입

1

4

Array 표현 예

1차원
2차원
3차원

 

 

2-1. Array reshape & flatten

  • Array의 shape의 크기를 변경함 (size는 같지만, dimension이 달라짐)

import numpy as np

test_matrix = [[1,2,3,4], [1,2,5,8]]
print(np.array(test_matrix).shape)
print(np.array(test_matrix).reshape(2,2,2))
print(np.array(test_matrix).reshape(-1,2).shape)  # -1 : size를 기반으로 row 개수 선정
print(np.array(test_matrix).flatten())  # 3차원 -> 1차원, 2차원 -> 1차원

(2,4)

[[[1 2],
  [3 4]],

 [[1 2],
  [5 8]]]

(4,2)

[1 2 3 4 1 2 5 8]

 

3. Indexing

  • List와 달리 이차원 배열에서 [0,0] 과 같은 표기법을 제공
  • Matrix 일경우 앞은 row, 뒤는 column을 의미함
a = np.array([[1, 2, 3], [4.5, 5, 6]], int)
print(a)
print(a[0,0])   # Two dimensional array representation #1
print(a[0][0])  # Two dimensional array representation #2

a[0,0] = 12  # Matrix 0,0에 12 할당
print(a)
a[0][0] = 5  # Matrix 0,0에 5 할당
print(a)

[[1 2 3]
 [4 5 6]]
1
1
[[12  2  3]
 [ 4  5  6]]
[[5 2 3]
 [4 5 6]]

 

 

4. Slicing

  • List와 달리 행과 열 부분을 나눠서 slicing이 가능함
  • Matrix의 부분 집합을 추출할 때 반복문 없이 할 수 있어 유용함
a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]], int)
a[:,2:]  # Row = 전체, Col = 2 이상
a[1,1:3] # Row = 1, Col = 1~2
a[1:3]   # Row = 1~2

 

 

 

 

 

Creation function, Operation functions, array opeartions, comparsions, Boolean index, fancy index, numpy data i/o

등 나머지는 Numpy - 2 에서 계속

간단한 선형대수의 수식들을 List Comprehesion을 사용하여 pythonic code 를 작성하려고 한다.

Linear Algebra codes - 1 은 처음 배웠을 때 작성한 것이라 부족한 부분이 있어 다시 작성하려고 한다.

2024.01.02 - [✍🏻Language/Python] - [Python for ML] Linear algebra codes

 

[Python for ML] Linear algebra codes

Vector representation of python Vector를 파이썬으로 표현하는 다양한 방법 존재 vector_a = [1, 2, 10] # List로 표현했을 경우 vector_b = (1, 2, 10) # Tuple로 표현했을 경우 vector_c = {'x':1, 'y':2, 'z':10} # dict로 표현했을

jijibae.tistory.com


 

1. Vector Size Check

  • vector 간 덧셈 또는 뺄셈 연산을 할 때, 연산이 가능한 사이즈인지를 확인하여 가능 여부를 True 또는 False로 반환
def vector_size_check(*vector_variables):
    return all(len(vector_variables[0]) == x 
                for x in [len(vector) for vector in vector_variables[1:]])

# 실행결과
print(vector_size_check([1,2,3], [2,3,4], [5,6,7])) 
print(vector_size_check([1, 3], [2,4], [6,7])) 
print(vector_size_check([1, 3, 4], [4], [6,7]))

True

Ture

Flase

 

 

2. Vector Addition

  • vector간 덧셈을 실행하여 결과를 반환함, 단 입력되는 vector의 갯수와 크기는 일정하지 않음

def vector_addition(*vector_variables):
    if vector_size_check(*vector_variables) == False:
        raise ArithmeticError
    return [sum(element) for element in zip(*vector_variables)]

# 실행결과
print(vector_addition([1, 3], [2, 4], [6, 7])) 
print(vector_addition([1, 5], [10, 4], [4, 7]))
print(vector_addition([1, 3, 4], [4], [6,7]))

[9, 14]
[15, 16]

ArithmeticError

 

3. Vector Substraction

  • vector간 뺄셈을 실행하여 결과를 반환함, 단 입력되는 vector의 갯수와 크기는 일정하지 않음

def vector_subtraction(*vector_variables):
    if vector_size_check(*vector_variables) == False:
        raise ArithmeticError
    return [2 * elements[0] - sum(elements) for element in zip(*vector_variables)]

# 실행결과
print(vector_subtraction([1, 3], [2, 4]))
print(vector_subtraction([1, 5], [10, 4], [4, 7]))

[-1, -1]
[-13, -6]

 

4. Scalar Vector Product

  • 하나의 scalar 값을 vector에 곱함, 단 입력되는 vector의 크기는 일정하지 않음

def scalar_vector_product(alpha, *vector_variables):
    return [alpha * row for row in vector_variables]

# 실행결과
print (scalar_vector_product(5,[1,2,3])
print (scalar_vector_product(3,[2,2]))
print (scalar_vector_product(4,[1]))

[5, 10, 15]
[6, 6]
[4]

 

5. Matrix Size Check

  • matrix 간 덧셈 또는 뺄셈 연산을 할 때, 연산이 가능한 사이즈인지를 확인하여 가능 여부를 True 또는 False로 반환함
  • 열 판단 and 행 판단
def matrix_size_check(*martix_variables):
    all([len(set(len(matrix[0]) for matrix in matrix_variables)) == 1]) and 
        all([len(matrix_variables[0]) == len(matrix) for matrix in matrix_variables])

# 실행결과
matrix_x = [[2, 2], [2, 2], [2, 2]]
matrix_y = [[2, 5], [2, 1]]
matrix_z = [[2, 4], [5, 3]]
matrix_w = [[2, 5], [1, 1], [2, 2]]

print (matrix_size_check(matrix_x, matrix_y, matrix_z)) 
print (matrix_size_check(matrix_y, matrix_z))
print (matrix_size_check(matrix_x, matrix_w))

False

True

True

 

6. Is Matrix Equal

  • 비교가 되는 n개의 matrix가 서로 동치인지 확인하여 True 또는 False를 반환함
  • all( ) 이 두 번인 이유 : matrix 형태이므로 row의 값이 다 같은지 확인하고, column이 모두 같은지도 확인

def is_matrix_equal(*matrix_variables):
    all([all([len(set(row)) == 1 for row in zip(*matrix)]) for matrix in zip(*matrix_variables)])
    
# 실행결과
matrix_x = [[2, 2], [2, 2]]
matrix_y = [[2, 5], [2, 1]]

print (is_matrix_equal(matrix_x, matrix_y, matrix_y, matrix_y))
print (is_matrix_equal(matrix_x, matrix_x))

False

True

 

7. Matrix Addition

  • matrix간 덧셈을 실행하여 결과를 반환함, 단 입력되는 matrix의 갯수와 크기는 일정하지 않음

def matrix_addition(*matrix_variables):
    if matrix_size_check(*matrix_variables) == False:
        raise ArithmeticError
    return [[sum(row) for row in zip(*matrix)] for matrix in zip(*matrix_variables)]
    
# 실행결과
matrix_x = [[2, 2], [2, 2]]
matrix_y = [[2, 5], [2, 1]]
matrix_z = [[2, 4], [5, 3]]

print (matrix_addition(matrix_x, matrix_y))
print (matrix_addition(matrix_x, matrix_y, matrix_z))

[[4, 7], [4, 3]]

[[6, 11], [9, 6]]

 

8. Matrix Subtraction

  • matrix간 뺄셈을 실행하여 결과를 반환함, 단 입력되는 matrix의 갯수와 크기는 일정하지 않음

def matrix_subtraction(*matrix_variables):
    if matrix_size_check(*matrix_variables) == False:
        raise ArithmeticError
        
    return [[2 * row[0] - sum(row) for row in zip(*matrix)] for matrix in zip(*matrix_variables)]
    
# 실행결과
matrix_x = [[2, 2], [2, 2]]
matrix_y = [[2, 5], [2, 1]]
matrix_z = [[2, 4], [5, 3]]

print(matrix_subtraction(matrix_x, matrix_y))
print(matrix_subtraction(matrix_x, matrix_y, matrix_z))

[[0, -3], [0, 1]]

[[-2, -7], [-5, -2]]

 

9. Matrix Transpose

  • matrix의 역행렬을 구하여 결과를 반환함, 단 입력되는 matrix의 크기는 일정하지 않음

def matrix_transpose(*matrix_variables):
    return [[element for element in row] for row in zip(*matrix_variables)]]
    
# 실행결과
matrix_w = [[2, 5], [1, 1], [2, 2]]
print(matrix_transpose(matrix_w))

[[2, 1, 2], [5, 1, 2]]

 

10. Scalar Matrix Product

  • 하나의 scalar 값을 matrix에 곱함, 단 입력되는 matrix의 크기는 일정하지 않음

def scalar_matrix_product(alpha, matrix_variable):
    return [[alpha * element for element in row] for row in matrix_x]

# 실행결과
matrix_x = [[2, 2], [2, 2], [2, 2]]
matrix_y = [[2, 5], [2, 1]]
matrix_z = [[2, 4], [5, 3]]
matrix_w = [[2, 5], [1, 1], [2, 2]]

print(scalar_matrix_product(3, matrix_x))
print(scalar_matrix_product(2, matrix_y))
print(scalar_matrix_product(4, matrix_z))
print(scalar_matrix_product(3, matrix_w))

[[6, 6], [6, 6], [6, 6]]
[[4, 10], [4, 2]]
[[8, 16], [20, 12]]
[[6, 15], [3, 3], [6, 6]]

 

11. Is Product Availability Matrix

  • 두 개의 matrix가 입력 되었을 경우, 두 matrix의 곱셈 연산의 가능 여부를 True 또는 False로 반환함
def is_product_availability_matrix(matrix_a, matrix_b):
     return len([colunm_vector for colunm_vector in zip(*matrix_a)]) == len(matrix_b)
     
# 실행 결과
matrix_x= [[2, 5], [1, 1]]
matrix_y = [[1, 1, 2], [2, 1, 1]]
matrix_z = [[2, 4], [5, 3], [1, 3]]

print(is_product_availability_matrix(matrix_y, matrix_x))
print(is_product_availability_matrix(matrix_y, matrix_z))
print(is_product_availability_matrix(matrix_z, matrix_x))
print(is_product_availability_matrix(matrix_z, matrix_w))
print(is_product_availability_matrix(matrix_x, matrix_x))

True

True

True

False

True

 

12. Matrix Product

  • 곱셈 연산이 가능한 두 개의 matrix의 곱셈을 실행하여 반환함
def matrix_product(matrix_a, matrix_b):
    return [[sum(x * y for x, y in zip(row, col)) for col in zip(*matrix_b)] for row in matrix_a]]
    
# 실행결과
matrix_x= [[2, 5], [1, 1]]
matrix_y = [[1, 1, 2], [2, 1, 1]]
matrix_z = [[2, 4], [5, 3], [1, 3]]

print(matrix_product(matrix_y, matrix_z))
print(matrix_product(matrix_z, matrix_x))
print(matrix_product(matrix_x, matrix_x))

[[9, 13], [10, 14]]
[[8, 14], [13, 28], [5, 8]]
[[9, 15], [3, 6]]