Python: Czy zgłaszanie wyjątków w __init__ jest złą formą?
128
Czy zgłaszanie wyjątków w ramach programu jest złą formą __init__? Jeśli tak, to jaka jest akceptowana metoda zgłaszania błędu, gdy pewne zmienne klasy są inicjowane jako Nonelub niepoprawnego typu?
W C zgłoszenie wyjątku w destruktorze jest złą formą, ale konstruktor powinien być w porządku.
Calyth
6
@Calyth, masz na myśli C ++ - w C. nie ma konstruktorów ani destruktorów
Alex Martelli
4
... lub wyjątki, jeśli o to chodzi.
Matt Wilding,
@Calyth: lol, proszę usuń to bezsensowne stwierdzenie złożone przez niewinną literówkę ;-)
lpapp
4
@lpapp yes. C ++. FML. I nie mogłem edytować tego komentarza. Internet powinien więc udokumentować moje idiotyzm;)
Calyth,
Odpowiedzi:
159
Zgłaszanie wyjątków w ramach __init__() jest absolutnie w porządku. Nie ma innego dobrego sposobu na wskazanie warunku błędu w konstruktorze, aw bibliotece standardowej istnieje wiele setek przykładów, w których zbudowanie obiektu może zgłosić wyjątek.
Klasa błędu, którą należy podnieść, zależy oczywiście od Ciebie. ValueErrornajlepiej, jeśli do konstruktora przekazano nieprawidłowy parametr.
Najczęściej używanymi wyjątkami w konstruktorze są ValueErrori TypeError.
Denis Otkidach
9
__init__Zwracam tylko uwagę, że nie jest konstruktorem, tylko inicjatorem. Możesz chcieć edytować linię Nie ma innego dobrego sposobu na wskazanie stanu błędu w konstruktorze, ...
Haris
26
Prawdą jest, że jedynym właściwym sposobem wskazania błędu w konstruktorze jest zgłoszenie wyjątku. Dlatego w C ++ i innych językach obiektowych, które zostały zaprojektowane z myślą o bezpieczeństwie wyjątków, destruktor nie jest wywoływany, jeśli w konstruktorze obiektu zostanie zgłoszony wyjątek (co oznacza, że inicjalizacja obiektu jest niekompletna). Często tak nie jest w językach skryptowych, takich jak Python. Na przykład poniższy kod zgłasza błąd AttributeError, jeśli funkcja socket.connect () nie powiedzie się:
Przyczyną jest to, że destruktor niekompletnego obiektu jest wywoływany po nieudanej próbie połączenia, przed zainicjowaniem atrybutu strumienia. Nie należy unikać wyrzucania wyjątków z konstruktorów, mówię tylko, że trudno jest napisać w Pythonie kod w pełni bezpieczny dla wyjątków. Niektórzy programiści Pythona całkowicie unikają używania destruktorów, ale to już kwestia innej debaty.
Ta odpowiedź jest naprawdę przydatna. Nie chodzi tylko o „zielone światło”, ale o przykład z „destruktorem”.
lpapp,
Twój kod Pythona zawodzi, __del__ponieważ jest źle napisany. Miałbyś dokładnie ten sam problem, gdybyś zrobił this->p_socket->close()w destruktorze w C ++. W C ++ nie zrobiłbyś tego - pozwoliłbyś obiektowi składowemu sam się zniszczyć. Zrób to samo w Pythonie.
Nie widzę powodu, dla którego miałaby to być zła forma.
Wręcz przeciwnie, jednym z wyjątków, które są znane z tego, że radzą sobie dobrze, w przeciwieństwie do zwracania kodów błędów, jest to, że konstruktorzy zwykle nie mogą zwracać kodów błędów . Tak więc przynajmniej w językach takich jak C ++ zgłaszanie wyjątków jest jedynym sposobem sygnalizowania błędów.
>>> f = file("notexisting.txt")Traceback(most recent call last):File"<stdin>", line 1,in<module>IOError:[Errno2]No such file or directory:'notexisting.txt'
Poza tym nie widzę żadnego powodu, dla którego miałoby to być uważane za złą formę.
Tak naprawdę nie ma innego sposobu, aby zasygnalizować, że coś poszło nie tak podczas inicjalizacji obiektu, niż zgłoszenie wyjątku.
W większości klas programów, w których stan klasy jest całkowicie zależny od danych wejściowych tej klasy, możemy spodziewać się podniesienia pewnego rodzaju wartości ValueError lub TypeError.
Klasy z efektami ubocznymi (np. Takie, które obsługują sieć lub grafikę) mogą powodować błąd w init, jeśli (na przykład) urządzenie sieciowe jest niedostępne lub nie można zapisać do obiektu kanwy. Wydaje mi się to rozsądne, ponieważ często chcesz wiedzieć jak najszybciej o warunkach awarii.
W niektórych przypadkach nie da się uniknąć błędów z init, ale zbyt dużo pracy w init to zły styl. Powinieneś rozważyć stworzenie fabryki lub pseudo-fabryki - prostej metody klasowej, która zwraca skonfigurowany obiekt.
Odpowiedzi:
Zgłaszanie wyjątków w ramach
__init__()
jest absolutnie w porządku. Nie ma innego dobrego sposobu na wskazanie warunku błędu w konstruktorze, aw bibliotece standardowej istnieje wiele setek przykładów, w których zbudowanie obiektu może zgłosić wyjątek.Klasa błędu, którą należy podnieść, zależy oczywiście od Ciebie.
ValueError
najlepiej, jeśli do konstruktora przekazano nieprawidłowy parametr.źródło
ValueError
iTypeError
.__init__
Zwracam tylko uwagę, że nie jest konstruktorem, tylko inicjatorem. Możesz chcieć edytować linię Nie ma innego dobrego sposobu na wskazanie stanu błędu w konstruktorze, ...Prawdą jest, że jedynym właściwym sposobem wskazania błędu w konstruktorze jest zgłoszenie wyjątku. Dlatego w C ++ i innych językach obiektowych, które zostały zaprojektowane z myślą o bezpieczeństwie wyjątków, destruktor nie jest wywoływany, jeśli w konstruktorze obiektu zostanie zgłoszony wyjątek (co oznacza, że inicjalizacja obiektu jest niekompletna). Często tak nie jest w językach skryptowych, takich jak Python. Na przykład poniższy kod zgłasza błąd AttributeError, jeśli funkcja socket.connect () nie powiedzie się:
Przyczyną jest to, że destruktor niekompletnego obiektu jest wywoływany po nieudanej próbie połączenia, przed zainicjowaniem atrybutu strumienia. Nie należy unikać wyrzucania wyjątków z konstruktorów, mówię tylko, że trudno jest napisać w Pythonie kod w pełni bezpieczny dla wyjątków. Niektórzy programiści Pythona całkowicie unikają używania destruktorów, ale to już kwestia innej debaty.
źródło
__del__
ponieważ jest źle napisany. Miałbyś dokładnie ten sam problem, gdybyś zrobiłthis->p_socket->close()
w destruktorze w C ++. W C ++ nie zrobiłbyś tego - pozwoliłbyś obiektowi składowemu sam się zniszczyć. Zrób to samo w Pythonie.Nie widzę powodu, dla którego miałaby to być zła forma.
Wręcz przeciwnie, jednym z wyjątków, które są znane z tego, że radzą sobie dobrze, w przeciwieństwie do zwracania kodów błędów, jest to, że konstruktorzy zwykle nie mogą zwracać kodów błędów . Tak więc przynajmniej w językach takich jak C ++ zgłaszanie wyjątków jest jedynym sposobem sygnalizowania błędów.
źródło
Biblioteka standardowa mówi:
Poza tym nie widzę żadnego powodu, dla którego miałoby to być uważane za złą formę.
źródło
Myślę, że jest to idealny przypadek dla wbudowanego
ValueError
wyjątku.źródło
Zgadzam się z powyższym.
Tak naprawdę nie ma innego sposobu, aby zasygnalizować, że coś poszło nie tak podczas inicjalizacji obiektu, niż zgłoszenie wyjątku.
W większości klas programów, w których stan klasy jest całkowicie zależny od danych wejściowych tej klasy, możemy spodziewać się podniesienia pewnego rodzaju wartości ValueError lub TypeError.
Klasy z efektami ubocznymi (np. Takie, które obsługują sieć lub grafikę) mogą powodować błąd w init, jeśli (na przykład) urządzenie sieciowe jest niedostępne lub nie można zapisać do obiektu kanwy. Wydaje mi się to rozsądne, ponieważ często chcesz wiedzieć jak najszybciej o warunkach awarii.
źródło
W niektórych przypadkach nie da się uniknąć błędów z init, ale zbyt dużo pracy w init to zły styl. Powinieneś rozważyć stworzenie fabryki lub pseudo-fabryki - prostej metody klasowej, która zwraca skonfigurowany obiekt.
źródło