클래스 생성자 오버로딩 하는법

2020. 9. 23. 04:56프로그래밍 언어/Python

반응형

C++에는 생성자의 오버로딩(Overloading)이라는게 존재한다.

class Person {
public:	
    string name;
    int age;
    bool gender;
    
    Person(); // 기본생성자
    Person(string name, int age, bool gender); // 생성자 오버로딩
}

두개의 생성자 모두 Person이라는 이름을 갖지만 전달되는 파라미터에 따라 구분되는 것이다.

class Person:
    def __init__(self):
        self.name = None
        self.age = None
        self.gender = None

    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
    
    def show(self):
        print('Name: ' + str(self.name))
        print('Age: ' + str(self.age))
        print('Gender: ' + 'Male' if self.gender else 'Female')


if __name__ == '__main__':
    p = Person()
    p.show()

파이썬에서도 생성자 오버로딩을 구현해 보았다. 하지만 위의 코드를 실행시켜보면 다음과 같은 에러가 출력된다.

TypeError: __init__() missing 3 required positional arguments: 'name', 'age', and 'gender'

생성자 __init__()을 실행 했을때 필요한 세개의 인자가 없다는 내용이다.

인자가 필요없는 기본 생성자가 있음에도 불구하고 인식을 못하고 있는것이다. 왜 그럴까?

 

파이썬에서는 기본적으로 생성자를 여러개 만들 수 없다. 여러개가 있다 하더라도 가장 밑에 있는 생성자만 인식해서 실행되고 PyCharm같은 IDE에서는 경고문구를 표시해준다. 따라서 여러가지 생성자를 만들기 위해서는 C++와 같은 방법으로는 불가능한 것이다.

 

그러면 파이썬에서는 어떻게 생성자 오버로딩 기능을 구현 할 수 있을까?


인자의 기본값 & if문

def __init__(self, name=None, age=None, gender=None, person_from_str=None):
    if person_from_str:
        person = person_from_str.split(',')
        self.name = person[0]
        self.age = int(person[1])
        self.gender = bool(int(person[2]))
    else:
        self.name = name
        self.age = age
        self.gender = gender

인자가 있는 생성자에서 인자에 기본값을 부여하고 그 값에 따른 분기를 처리하는 방식이다. 명료하고 보기좋게 작성할 수 있지만 크게 두가지 단점이 있다.

 

1. 코드의 반복과 추가 분기

객체의 멤버 변수를 초기화 해주는 부분이 반복된다. 그리고 모든 다른 경우에 대해 if문으로 분기처리를 해주어야 한다.

2. 모든 인자의 기본값이 None인 객체가 생성 가능

위와 같은 방법으로 생성자를 작성했을때, 아무런 파라미터 없이 생성자를 호출할 경우 모든 멤버 변수가 None인 객체가 생성된다. 이러한 방식으로 생성자를 사용하지 않거나 분기를 추가해주면 해결 할 수 있지만, 이는 좋지 않은 방법이며 생성자를 추가하고 싶을 때에는 더욱 골치 아파진다.


@classmethod 데코레이터

@classmethod
def from_str(cls, person_from_str) -> 'Person':
	person = person_from_str.split(',')
	return cls(name=person[0], age=int(person[1]), gender=bool(int(person[2])))

#
#
#

if __name__ == '__main__':
    p = Person.from_str('John Doe, 25, 1')
    p.show()

이 방법은 클래스 메서드 데코레이터를 이용해 클래스 메서드로 또다른 생성자를 구현하는 방법이다.

 

클래스 메서드(class method)란 객체가 아닌 클래스 자체에 묶여있는(bound to) 메서드이다. (자세한 내용은 https://www.programiz.com/python-programming/methods/built-in/classmethod 참조)

 

이 방식은 기본값과 if문을 활용한 이전 방법의 단점들을 모두 보완하는 동시에 메서드에 붙여줌으로써 생성자의 역할을 좀 더 명료하게 나타낼 수 있다.


참조

https://stackoverflow.com/questions/141545/how-to-overload-init-method-based-on-argument-type
 

How to overload __init__ method based on argument type?

Let's say I have a class that has a member called data which is a list. I want to be able to initialize the class with, for example, a filename (which contains data to initialize the list) or wi...

stackoverflow.com

https://medium.com/@shamir.stav_83310/overloading-constructors-in-python-17e42b7430b6

 

Overloading constructors in Python

TL;DR “Overloading” (or rather, mimicking overloading) __init__ is not a good idea. Use factory methods with the classmethod decorator.

medium.com

 

반응형