Korzystanie z właściwości w tej samej klasie jest niebezpieczne, ponieważ może prowadzić do błędów, które są bardzo trudne do debugowania.
Jeśli gracz pobierający nieruchomość rzuca AttributeError
, wówczas AttributeError
zostaje po cichu schwytany i zostaje wywołana __getattr__
. Zwykle powoduje to, że __getattr__
kończy się niepowodzeniem z wyjątkiem, ale jeśli masz wyjątkowo pecha, to nie, a nawet nie będziesz w stanie łatwo prześledzić problemu z powrotem do __getattr__
.
Dopóki twoje mienie własnościowe nie jest trywialne, nigdy nie możesz być 100% pewny, że nie wyrzuci AttributeError
. Wyjątek może być wyrzucony na kilka poziomów.
Oto co można zrobić:
- Unikaj używania właściwości i
__getattr__
w tej samej klasie.
- Dodaj
try ... except
blok do wszystkich pobierające własności, które nie są trywialne
- Przechowywać pobierające własności proste, więc wiesz, że nie rzuci
AttributeError
- Napisz swoją własną wersję
@property
dekoratora, który łapie AttributeError
i ponownie wyrzuca go jako RuntimeError
.
Zobacz także http://blog.devork.be/2011/06/using-getattr-and-property_17.html
EDIT: W przypadku gdy ktoś rozważa rozwiązanie 4 (czego nie polecam), można to zrobić tak:
def property_(f):
def getter(*args, **kwargs):
try:
return f(*args, **kwargs)
except AttributeError as e:
raise RuntimeError, "Wrapped AttributeError: " + str(e), sys.exc_info()[2]
return property(getter)
Następnie użyj @property_
zamiast @property
w klasach, które zastępują __getattr__
.
Powiedziałbym, że niepowodzenie dokumentacji polega na tym, że "AttributeError" może zostać podniesiony w '__get__' (co skutecznie tutaj robisz), ale nie wyjaśnia, jaki efekt ma. Prawdopodobnie powinno się powiedzieć, że 'AttributeError' sygnalizuje, że powinien zachowywać się tak, jakby nie znaleziono tego atrybutu (aby jak zwykle próbowano wykonać kopię" __getattr__ ") - jeśli to nie jest pożądane zachowanie, to powinieneś podnieść coś innego. – James