Chtěl bych vědět něco více o výjimkách a jejich argumentech

Dotaz z konference: Potřebuji vědět jaký počet argumentů mají jednotlivé exceptions. [...] Kde najdu nějaký seznam argumentů k jednotlivým exceptions?

Jedna odpověď

Myslím, že se to vždy předává jako tuple, jen někdy, často má tuple jen jeden prvek, takže se automaticky přiřazuje do proměnné jako řetězec. Jinak funguje toto:

    >>> dir(OSError())
    ['__doc__', '__getitem__', '__init__', '__module__', '__str__', 'args', 
    'errno','filename', 'strerror']
    >>> dir(ArithmeticError())
    ['__doc__', '__getitem__', '__init__', '__module__', '__str__', 'args']

Ty poslední polozky bez __, to jsou ty, co se předávají v tuple.

Druhá odpověď

Standardní třída Exceptions() může mít libovolný počet argumentů. Záleží na uživateli, kolik jich použije.

Přístup k argumentům

Vnitřně se uchovávají v podobě n-tice (tuple, tedy později neměnitelné struktury). Je přístupná prostřednictvím atributu args. Příklad:

   >>> e = Exception(1, 'ahoj', 3)
   >>> e
   <exceptions.Exception instance at 0x00925F80>  
   >>> e.args
   (1, 'ahoj', 3)
   >>> type(e.args)
   <type 'tuple'>

Jejich konkrétní počet můžu zjistit stejně, jako zjišťuji počet prvků n-tice:

    >>> len(e.args)
    3

Indexováním můžeme zpřístupňovat jednotlivé argumenty z args (jako u každé běžné n-tice).:

   >>> e.args[0]
   1
   >>> e.args[1]
   'ahoj'
   >>> e.args[2]
   3
   >>> e.args[3]
   Traceback (most recent call last):
     File "<stdin>", line 1, in ?
   IndexError: tuple index out of range

Protože je definována metoda __getitem__(), můžeme argumenty zpřístupňovat i indexováním samotného objektu:

   >>> e[0]
   1
   >>> e[1]
   'ahoj'
   >>> e[2]
   3
   >>> e[3]
   Traceback (most recent call last):
     File "<stdin>", line 1, in ?
   IndexError: tuple index out of range

Zobrazení

Metoda __str__() je definována tak, aby se zobrazila řetězcová reprezentace argumentů. Její výsledek typicky získáváme prostřednictvím zabudované funkce str() nebo dosazením do příkazu print:

    >>> print e
    (1, 'ahoj', 3)
    >>> str(e)
    "(1, 'ahoj', 3)"

Definice vlastní výjimky

S výše uvedenými znalostmi bych mohl definovat svou vlastní bázovou třídu pro výjimky s podobnými vlastnostmi takto:

    class MyException:
       """Common base class for all MY exceptions."""

       def __init__(self, *args):
           self.args = tuple(args)

       def __getitem__(self, i):
           return self.args[i]

       def __str__(self):
           return 'My exception: ' + str(self.args)

Standardní třída Exceptions nemá pythonovský zdrojový text, protože je implementována přímo v jádře jazyka, ale její definice by mohla vypadat téměř stejně -- takto:

   class Exception:
      """Common base class for all exceptions."""

      def __init__(self, *args):
          self.args = tuple(args)

      def __getitem__(self, i):
          return self.args[i]

      def __str__(self):
          return str(self.args)

Ruční vyvolání výjimky

Vyvolání výjimky a její odchycení může v dokumentaci vypadat trochu zmateně, protože se dříve výjimky realizovaly v podobě řetězcových objektů. V současnosti se mají vždy používat výjimky odvozené od Exception. Při vyvolávání výjimky si typicky objekt reprezentující výjimku vytvářím -- doporučuje se zápis vyjadřující konstrukci nového objektu:

   try:
      ...
      raise Exception(1, 'ahoj', 3)
      ...

Odchytávání výjimky

Při odchytávání výjimky se definuje především typ odchytávané výjimky (tj. typ objektu výjimky):

    except Exception:
      print 'Nastala vyjimka.'

Volitelně můžeme uvést jméno, které bude spojeno s konkrétní instancí výjimky (s konkrétním objektem). Jeho prostřednictvím si můžeme zpřístupnit argumenty výjimky:

   except Exception, e:
      # e zpřístupňuje objekt výjimky.
      print e.args
      print e
      print len(e.args)
      for arg in e.args:
          print arg

... případně po zpracování argumentů můžeme výjimku znovu zápisem raise bez argumentů nebo, v našem případě, zápisem "raise e". V druhém případě můžu před opakovaným vyvoláním výjimky přiřadit do e.args novou n-tici, takže vlastně můžu upřesnit nebo zcela změnit argumenty výjimky. Systému je to jedno, ten se o argumenty výjimky nestará. Je to čistě uživatelská záležitost.

Všechny ostatní formy zápisu (pokud se nepletu) jsou dědictvím minulosti a nedoporučuje se je používat.