Python, rzutowanie typu i obsługa wyjątku

0

Cześć.
chciałem coś zrobić chytrze po pythonowemu i kod poniżej działa, ale...
next zwraca mi referencję na element listy kont, który jest typu Object, czyli gdy po next(costam). <- to po kropce nie rozwija mi w pyCharm'ie dostępnych dla mojej klasy pól i metod. Można z tym żyć, ale zapewne można to życie uczynić łatwiejszym.

    def wplacNaKonto(self, nr: str, kwota: Decimal):
        next(k for k in self.getKonta() if k.nr == nr).wplac(kwota)

Jak zrobię coś takiego:

        next(Konto(k) for k in self.getKonta() if k.nr == nr).wplac(kwota)

lub

        zk = next(k for k in self.getKonta() if k.nr == nr)
        if zk:
               zk.wplac(kwota)

to moje znalezione konto jest kopią oryginalnego obiektu i wpłacam do kopii, a w oryginale saldo pozostaje bez zmian. Dodatkowo, w drugim przypadku rzuca Exception od razu w pierwszej linii, jeśli nr nie odpowiada żadnemu numerowi konta na liście zanim sprawdzę warunek instrukcją if.
Intuicja podpowiada mi, że początkowy kod jest poprawny, tylko trzeba jakoś ładnie rzutować typ, ale trochę wymiękłem jak to elegancko zrobić. Może ktoś z bardziej doświadczonych python'owców podpowie?

3

next obsługuje drugi, opcjonalny argument, który będzie zwrócony zamiast rzucenia StopIteration jeśli iterator się wyczerpie

>>> it = iter(range(2))
>>> next(it)
0
>>> next(it)
1
>>> next(it)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>
>>> it = iter(range(2))
>>> next(it, 'default')
0
>>> next(it, 'default')
1
>>> next(it, 'default')
'default'

Czyli u ciebie:

zk = next((k for k in self.getKonta() if k.nr == nr), None)
if zk:
    zk.wplac(kwota)
2

@szumek:
Nie wiem jaki Masz tam ten kod, ale w takim uproszczonym przypadku, next zwraca odpowiedni typ:

class A:
    pass


class B:
    a1 = A()
    a2 = A()
    def b(self):
        return [self.a1, self.a2]
    def do(self):
        print(type(next(e for e in self.b())))  # -> <class '__main__.A'>
1
szumek napisał(a):

Cześć.

chciałem coś zrobić chytrze po pythonowemu i kod poniżej działa, ale...
next zwraca mi referencję na element listy kont, który jest typu Object, czyli gdy po next(costam). <- to po kropce nie rozwija mi w pyCharm'ie dostępnych dla mojej klasy pól i metod. Można z tym żyć, ale zapewne można to życie uczynić łatwiejszym.

    def wplacNaKonto(self, nr: str, kwota: Decimal):
        next(k for k in self.getKonta() if k.nr == nr).wplac(kwota)

Jak zrobię coś takiego:

        next(Konto(k) for k in self.getKonta() if k.nr == nr).wplac(kwota)

to moje znalezione konto jest kopią oryginalnego obiektu i wpłacam do kopii, a w oryginale saldo pozostaje bez zmian.

Towrzysz kopię konta, czego oczekiwałeś?

        zk = next(k for k in self.getKonta() if k.nr == nr)
        if zk:
               zk.wplac(kwota)

Dodatkowo, w drugim przypadku rzuca Exception od razu w pierwszej linii, jeśli nr nie odpowiada żadnemu numerowi konta na liście zanim sprawdzę warunek instrukcją if.

Jak nie znajdzie to leci wyjątrek StopIteration, @Spearhead podał jak zrobić, by zamiast tego zwracał domyślną wartość. Czytaj dokumentację.

Intuicja podpowiada mi, że początkowy kod jest poprawny, tylko trzeba jakoś ładnie rzutować typ, ale trochę wymiękłem jak to elegancko zrobić. Może ktoś z bardziej doświadczonych python'owców podpowie?

Jakie rzutowanie? W pythonie? Typ się zgadza, tylko PyCharm głupieje bo język jest dynamicznie typowany i nie wie czego oczekiwać. Możesz sprawdzić czy dodanie type hint dla wartości zwracanej z getKonta pomoże.

0

Ostatecznie wymyśliłem coś takiego:

    def wplacNaKonto(self, nr: str, kwota: Decimal):
        zk = next((k for k in self.getKonta() if k.nr == nr), None)
        assert zk, f'brak konta o nr: {nr}'
        if zk:
            zk.wplac(kwota)

a w metodzie, która namieszała pozwalając omyłkowo dodać stringa zamiast obiekt typu Konto zrobiłem walidację typu

    def dodajKonto(self, konto: Konto):
        self.__konta.append(konto)

Dziękuję Wam za sugestie.
BTW. Czy Decimale trzeba pilnować z błędami zaokrągleń?

1

BTW. Czy Decimale trzeba pilnować z błędami zaokrągleń?

Chyba .quantize() jest tym czego szukasz :)

`

1
szumek napisał(a):

Ostatecznie wymyśliłem coś takiego:

    def wplacNaKonto(self, nr: str, kwota: Decimal):
        zk = next((k for k in self.getKonta() if k.nr == nr), None)
        assert zk, f'brak konta o nr: {nr}'
        if zk:
            zk.wplac(kwota)

a w metodzie, która namieszała pozwalając omyłkowo dodać stringa zamiast obiekt typu Konto zrobiłem walidację typu

    def dodajKonto(self, konto: Konto):
        self.__konta.append(konto)

Dziękuję Wam za sugestie.
BTW. Czy Decimale trzeba pilnować z błędami zaokrągleń?

  1. Skoro robisz asercję to ten if jest zbędny bo nigdy nie dojrze do if zk z pustym zk.

  2. A propos samej asercji, to czy to właściwy wybór zależy od kodu. Jeżeli zakładasz, że konto zawsze powinno istnieć, to jest to do zaakceptowanie. Ale jeżeli kontro może, ale nie musi istnieć, to lepiej to stwierdzać sygnalizować "zwykłym" wyjątkiem. Ogółem asercja jako taka średnio pasuje do dynamicznego języka jakim jest Python

  3. Nie mieszaj polskiego z angielskim w identyfikatorach, to getKonta strasznie kłuje w oczy. Proponuję wszystko nazywać po angielsku, jak dobre praktyki nakazują.

  4. https://stackoverflow.com/questions/7560455/decimals-to-2-places-for-money-in-python-3

0

@nalik: Mam takie przyzwyczajenie z C oraz Pascala/Delphi. Tam rzutowało się jeden wskaźnik na drugi. W Pascalu konstrukcja typwsk(innytypwsk) jest bliźniaczo podobna do tworzenia nowej instancji klasy Konto(k).
Dzięki za zainteresowanie.
W C:

    KlasaBazowa* wsk1 = &obiekt;
    KlasaPochodna* wsk2 = (KlasaPochodna*) wsk1;

W Pascal'u:

    KlasaBazowaPtr = ^KlasaBazowa;
    KlasaPochodnaPtr = ^KlasaPochodna;
    
     KlasaBazowaPtr wsk1 = @obiekt;
     KlasaPochodnaPtr wsk2 = KlasaPochodnaPtr(wsk1);
0
szumek napisał(a):

a w metodzie, która namieszała pozwalając omyłkowo dodać stringa zamiast obiekt typu Konto zrobiłem walidację typu

    def dodajKonto(self, konto: Konto):
        self.__konta.append(konto)

Gdzie ta walidacja typu? Mam nadzieję, że nie chodzi ci o : Konto, to to jest jedynie type hint.

1
szumek napisał(a):

[...] Asercji nie złapię catch'em. [...]

A niby czemu?

try:
	assert 1==0, 'x'
except AssertionError:
	print('y')

Inna sprawa, że to nic mądrego...

1 użytkowników online, w tym zalogowanych: 0, gości: 1