Jak przeciążyć konstruktor ?

Odpowiedź

Nie da się 🙂

Nie lubię pytań tego typu bo jest to troszkę celowe wprowadzanie kandydata w błąd. Jakby jednak nie patrzeć język Python nie przewiduje możliwości przeciążania czegokolwiek.

  • Nie można przeciążyć funkcji
  • Nie można przeciążyć metody
  • Nie można przeciążyć również konstruktora (bo on także jest metodą)

Jest to troszkę problematyczne dla osób programujących wcześniej w innych językach takich jak C++ czy Java. Jeśli Python jest pierwszym językiem jakiego się uczysz brak możliwości przeciążania konstruktora będzie dla Ciebie czymś naturalnym.

Ale co to jest to przeciążanie?

Sprawa wygląda następująco. Załóżmy, że mamy funkcję o swojskiej nazwie ”przeciąż mnie”. Wygląda ona tak:

def przeciaz_mnie(param1):
    print(param1)

Jak widać przyjmuje ona jeden parametr. W innych językach (ale nie w Pythonie), można by przeciążyć taką funkcję. Przeciążyć czyli napisać drugą funkcję o takiej samej nazwie, ale przyjmującą inne parametry. Mogłaby wyglądać tak:

def przeciaz_mnie(param1, param2):
    print(param1)
    print(param2)

W innych językach z silnym typowaniem przeciąża się nie tylko ilość parametrów, ale również ich typ. Jednak powtórzmy to jeszcze raz głośno W Pythonie nie ma przeciążania.

Jak sobie z tym radzić?

Czy brak możliwości przeciążania konstruktorów oznacza, że jesteśmy skazani tylko na jedną implementację konstruktora dla danej klasy ? Oczywiście, że nie. To jest Python tu można wszytko. No może poza przeciążaniem he he. Jednak jestem pewien, że pod koniec tego wpisu stwierdzisz, że wcale nie jest ono Ci potrzebne. Teraz kila sposób na obejście naszego problemu.

Wartości domyślne

Aby umożliwić wywołanie konstruktora dla jednego lub dla dwóch parametrów można jednemu z nich przypisać wartość domyślną:

class Przeciazanie:
  def __init__(self, param1, param2=None):
    print(param1)
    if(param2):
      print(param2)

jeden_param = Przeciazanie('param1')
dwa_param = Przeciazanie('param1', 'param2')

Uruchom w edytorze

Output:

param1
param1
param2

Dzięki takiemu trikowi udało nam się skonstruować obiekt zarówno podając jeden jak i dwa parametry. Takie rozwiązanie jest jednak mało elastyczne. Bardzo szybko duża ilość parametrów z wartościami domyślnymi byłaby trudna do zarządzania.

*args i **kwargs

Użycie argskwargs jako parametrówkonstruktorze sprawia, że nasz konstruktor można wywołać praktycznie z dowolnymi parametrami. Dodatkowo przypomnę, że argskwargs to tylko nazwy. Możesz nazywać te parametry jak sobie tylko chcesz, byle nie zapomnieć o poprzedzających je gwiazdkach. Pamiętaj jednak, że PEP8 zaleca by zostać przy standardowych nazwach argskwargs. Dobra koniec gadania czas na przykład:

class Przeciazanie:
  def __init__(self, *args, **kwargs):
    print(args)
    print(kwargs)

z = Przeciazanie()
print('---------------')
x = Przeciazanie('param1', 'param2')
print('---------------')
c = Przeciazanie(param3='param3')

Uruchom w edytorze

Output:

()
{}
---------------
('param1', 'param2')
{}
---------------
()
{'param3': 'param3'}

Jak widać nie ma problemu by wywołać konstruktor z jednym, dwoma czy z zerową ilością parametrów. Rozwiązanie świetne, ale to w dalszym ciągu jeden konstruktor. Co jeśli chciałbyś mieć różne konstruktory dla różnej ilości parametrów. Mimo braku możliwości przeciążaniaPythonie możemy osiągnąć coś bardzo zbliżonego.

@classmethod

Ten dekorator służy do tworzenia metod statycznych (czyli takich należących do klasy). Jest bliskim kolegą innego dekoratora@staticmethod, który w sumie robi prawie to samo. Różnica między nimi jest taka, że ten pierwszy ma dostęp do innych statycznych pól i metod klasy. My użyjemy ”classmethod” jako alternatywa dla wielu konstruktorów:

class Przeciazanie:
  def __init__(self, *args, **kwargs):
    print(args)
    print(kwargs)

  @classmethod
  def konstruktor1(cls):
    return cls('konstruktor1')

  @classmethod
  def konstruktor2(cls):
    return cls('konstruktor2')

  @classmethod
  def konstruktor3(cls):
    return cls('konstruktor3')

z = Przeciazanie.konstruktor1()
z = Przeciazanie.konstruktor2()
z = Przeciazanie.konstruktor3()

Uruchom w edytorze

Output:

('konstruktor1',)
{}
('konstruktor2',)
{}
('konstruktor3',)
{}

Jak widać brak możliwości przeciążania funkcjimetod (w tym także konstruktora) wcale nie jest taki straszny. Można z tym żyć. Twórcy języka w myśl zasady ”Readability counts” celowo zrezygnowali z tej opcji by ułatwić czytelność. Biorąc pod uwagę, że 80% czasu spędzamy na czytaniu kodu przyznajcie, że ma to sens. Python jest też na tyle elastyczny, że brak przeciążania po jakimś czasie nie będzie Ci już przeszkadzać.