Python Language
Python-antimönster
Sök…
Överflödigt utom klausul
Undantag är kraftfulla, men en enda övergripande utom klausul kan ta allt bort i en enda rad.
try: res = get_result() res = res[0] log('got result: %r' % res) except: if not res: res = '' print('got exception')
Detta exempel visar tre symtom på antipattern:
- Den
except
utan undantag typ (linje 5) kommer att fånga även friska undantag, inklusiveKeyboardInterrupt
. Det kommer i vissa fall att förhindra att programmet går ut. - Undantagsblocket upprepar inte felet, vilket innebär att vi inte kan se om undantaget kommer från
get_result
eller omres
var en tom lista. - Värst av allt, om vi var oroliga för att resultatet skulle vara tomt, har vi orsakat något mycket värre. Om
get_result
misslyckas, kommerres
att förbli helt oförändrat, och hänvisningen tillres
i undantagsblocket kommer att höjaNameError
, vilket helt maskerar det ursprungliga felet.
Tänk alltid på vilken typ av undantag du försöker hantera. Ge undantagssidan en läs och få en känsla för vilka grundläggande undantag som finns.
Här är en fast version av exemplet ovan:
import traceback try: res = get_result() except Exception: log_exception(traceback.format_exc()) raise try: res = res[0] except IndexError: res = '' log('got result: %r' % res)
Vi fångar mer specifika undantag och ökar om det behövs. Några fler rader, men oändligt mer korrekt.
Titta innan du hoppar med processorintensiv funktion
Ett program kan enkelt slösa bort tid genom att ringa en processorintensiv funktion flera gånger.
Ta till exempel en funktion som ser ut så här: det returnerar ett heltal om value
kan producera ett, annars None
:
def intensive_f(value): # int -> Optional[int]
# complex, and time-consuming code
if process_has_failed:
return None
return integer_output
Och det kan användas på följande sätt:
x = 5
if intensive_f(x) is not None:
print(intensive_f(x) / 2)
else:
print(x, "could not be processed")
print(x)
Även om detta kommer att fungera, har det problemet att ringa intensive_f
, vilket fördubblar tiden för koden att köra. En bättre lösning skulle vara att få returneringsvärdet för funktionen i förväg.
x = 5
result = intensive_f(x)
if result is not None:
print(result / 2)
else:
print(x, "could not be processed")
Men ett tydligare och kanske mer pytoniskt sätt är att använda undantag, till exempel:
x = 5
try:
print(intensive_f(x) / 2)
except TypeError: # The exception raised if None + 1 is attempted
print(x, "could not be processed")
Här behövs ingen tillfällig variabel. Det kan ofta vara att föredra att använda en assert
uttalande och fånga AssertionError
istället.
Ordbok nycklar
Ett vanligt exempel på var detta kan hittas är att få åtkomst till ordboksnycklar. Jämför till exempel:
bird_speeds = get_very_long_dictionary()
if "european swallow" in bird_speeds:
speed = bird_speeds["european swallow"]
else:
speed = input("What is the air-speed velocity of an unladen swallow?")
print(speed)
med:
bird_speeds = get_very_long_dictionary()
try:
speed = bird_speeds["european swallow"]
except KeyError:
speed = input("What is the air-speed velocity of an unladen swallow?")
print(speed)
Det första exemplet måste titta igenom ordboken två gånger, och eftersom det här är en lång ordbok kan det ta lång tid att göra det varje gång. Den andra kräver endast en sökning i ordboken och sparar därmed mycket processortid.
Ett alternativ till detta är att använda dict.get(key, default)
, men många omständigheter kan kräva mer komplexa åtgärder för att göra om nyckeln inte finns