2015-06-10 5 views
2

Почему я получаю разные результаты для следующих двух фрагментов коды (Python 3.4):Передача аргументов ключевых слов для пользовательских исключений - аномалия

class MainError(Exception): 
    def __init__(self, msg, **parms): 
     super().__init__() 
     self.msg = msg 
     self.parms = parms 
     print('Parms original', parms) 

    def __str__(self): 
     return self.msg + ':' + str(self.parms) 

class SubError(MainError): 
    def __init__(self, msg, **parms): 
     super().__init__(msg, **parms) 

try: 
    raise SubError('Error occured', line = 22, col = 11) 
except MainError as e: 
    print(e) 


>>> 
Parms original {'line': 22, 'col': 11} 
Error occured:{'line': 22, 'col': 11} 

А:

class MainError(Exception): 
    def __init__(self, msg, **args): 
     super().__init__() 
     self.msg = msg 
     self.args = args 
     print('Parms original', args) 

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

class SubError(MainError): 
    def __init__(self, msg, **args): 
     super().__init__(msg, **args) 

try: 
    raise SubError('Error occured', line = 22, col = 11) 
except MainError as e: 
    print(e) 


>>> 
Parms original {'line': 22, 'col': 11} 
Error occured:('line', 'col') 

ответ

2

Это потому, что арг об ошибках перезаписывается путем преобразования их в кортеж Python на уровне C.

Вот класс BaseException для Python: https://hg.python.org/cpython/file/tip/Objects/exceptions.c

Начиная со строки 31, мы видим следующее:

static PyObject * 
BaseException_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 
{ 
    PyBaseExceptionObject *self; 

    self = (PyBaseExceptionObject *)type->tp_alloc(type, 0); 
    if (!self) 
     return NULL; 
    /* the dict is created on the fly in PyObject_GenericSetAttr */ 
    self->dict = NULL; 
    self->traceback = self->cause = self->context = NULL; 
    self->suppress_context = 0; 

    if (args) { 
     self->args = args; 
     Py_INCREF(args); 
     return (PyObject *)self; 
    } 

    self->args = PyTuple_New(0); 
    if (!self->args) { 
     Py_DECREF(self); 
     return NULL; 
    } 

    return (PyObject *)self; 
} 

Аналогично, вызов инициализации имеет ту же преобразование кортежа:

BaseException_init(PyBaseExceptionObject *self, PyObject *args, PyObject *kwds) 
{ 
    PyObject *tmp; 

    if (!_PyArg_NoKeywords(Py_TYPE(self)->tp_name, kwds)) 
     return -1; 

    tmp = self->args; 
    self->args = args; 
    Py_INCREF(self->args); 
    Py_XDECREF(tmp); 

    return 0; 
} 

Короче говоря, self.args преобразуется в кортеж, который преобразуется обратно в строку, что вызывает разницу.

Класс BaseException вызывается для (как мне кажется) всех оболочек методов как обязательный аргумент.

Это заметно, если передать его не-Iterable аргумент (например, целое число):

>>> class CustomException(Exception): 
...  def __init__(self): 
...   super(CustomException, self).__init__('a') 
...   self.args = 1 
...  def __repr__(self): 
...   print(self.args) 
...   return '' 
... 
>>> CustomException() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 4, in __init__ 
TypeError: 'int' object is not iterable 

Мораль: Не называйте ваши переменные слова, которые постоянно переопределены и являются ключевыми термины класс, который вы используете.