LINUX.ORG.RU

except as err, почему это работает?

 


1

1
class CXError(Exception):
    pass

class ResponseKeyError(CXError):
    def __init__(self, random_arg):
        pass
        
def test(arg):
    i = {'privet': 'poka'}
    try:
        i[arg]
    except KeyError:
        raise ResponseKeyError(arg)

try:
    test('blablabla')
except ResponseKeyError as err:
    print(err)

python3 test.py

blablabla

Каким загадочным образом работает print(err), если __init__ всегда возвращает None?

★★★★★

Последнее исправление: steemandlinux (всего исправлений: 3)

если __init__ всегда возвращает None?

Разве __init__ вообще должен что-то возвращать, а его результат использоваться? Это конструктор и его задача - иницилиазировать обьект, уже имеющийся в памяти и имеющий вполне определенный тип. Если тебе надо управлять созданием обьектов, то копай глубже - в метаклассы и метод __new__.

crarkie
()

__init__ это не конструктор, это метод-инициализатор уже созданного экземпляра класса. Конструктор это метод __new__.

Virtuos86 ★★★★★
()
Ответ на: комментарий от crarkie

Это конструктор и его задача - иницилиазировать обьект, уже имеющийся в памяти и имеющий вполне определенный тип.

Ну и мешанина. Тащемта конструктор и должен создавать инстанс.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

Ну и мешанина. Тащемта конструктор и должен создавать инстанс.

Выделением памяти под обьект занимается не конструктор. Конструктор получает уже готовую память. Т.е обьект, но в неизвестном состоянии. Его задача - привести обьект в корректное начальное состояние.

crarkie
()
Ответ на: комментарий от steemandlinux

__init__() should return None

Что из этого непонятно? return None = ничего не возвращает. Для упрощения можешь считать так, хотя на самом деле все чуточку сложнее. Но не намного.

crarkie
()
Ответ на: комментарий от steemandlinux

Это толстый троллинг или в Python ты совсем не шаришь?

ResponseKeyError as err
err в данном случае становится локальной переменной к scope except'а. except перехватил ResponseKeyError и присвоил исключение переменной err. Что не так?

crarkie
()
Ответ на: комментарий от steemandlinux

err передался в print(err), там же никакой связи

Я так понял, тут два разных объекта с именем err. Один err - это объект типа ResponseKeyError. А тот err, что в параметре __init__ - не используется.

anonymous
()
Ответ на: комментарий от crarkie

Проблема в том, что выводится именно err из __init__, а не что-то другое. Почему print(err) вывел err из __init__, а не адрес класса?

steemandlinux ★★★★★
() автор топика
Последнее исправление: steemandlinux (всего исправлений: 1)
Ответ на: комментарий от crarkie

Действительно работает.

class ResponseKeyError(Exception):
        def __init__(self, err):
            pass

try:
     raise ResponseKeyError("123")
except ResponseKeyError as err1:
     print(err1)
anonymous
()
Ответ на: комментарий от anonymous

Но если в init добавить ещё один параметр, то уже не пашет.

Вру.

anonymous
()
Ответ на: комментарий от crarkie
class CXError(Exception):
    pass

class ResponseKeyError(CXError):
    def __init__(self, err):
        pass
        
def test(arg):
    i = {'privet': 'poka'}
    try:
        i[arg]
    except KeyError:
        raise ResponseKeyError(arg)

try:
    test('blablabla')
except ResponseKeyError as err:
    print(err)

python3 test.py blablabla

steemandlinux ★★★★★
() автор топика
Последнее исправление: steemandlinux (всего исправлений: 1)

А на родительский класс посмотреть слабо? Ищи метод __str__

maxim2
()
Ответ на: комментарий от anonymous

Действительно работает.

Думаю, это как-то связано с __new__.

anonymous
()
Ответ на: комментарий от anonymous
class RError(Exception):
    def __init__(self, err):
            super(RError, self).__init__('222')

try:
    raise RError("123")
except RError as err1:
    print(err1)

Похоже, будто если забыть самому вызвать родительский конструктор, то он вызывается автоматически, а параметры из нашего передаются в него.

crarkie
()
Ответ на: комментарий от crarkie

Похоже, будто если забыть самому вызвать родительский конструктор, то он вызывается автоматически, а параметры из нашего передаются в него.

Причем работает просто

print(ResponseKeyError("123"))
Но если заменить родительский класс, то уже не работает.

anonymous
()
Ответ на: комментарий от anonymous

Похоже на магию внутренностей Python. Возможно, класс Exception обрабатывается там как-то по особому. Потому что с обычными классами такого поведения нет.

crarkie
()
Ответ на: комментарий от crarkie

print печатает только строки из класса в случае с исключениями?

steemandlinux ★★★★★
() автор топика
Ответ на: комментарий от crarkie

Да никакой здесь магии нет, банальное незнание матчасти.

У класса BaseException определен метод __new__, который и обрабатывает параметры переданные при создании объекта исключения.

Естественно метод __init__, определенный в дочернем классе никак не влияет на поведение метода __new__ в базовом классе.

anonymous
()
Ответ на: комментарий от crarkie

Ну и мешанина. Тащемта конструктор и должен создавать инстанс.

Выделением памяти под обьект занимается не конструктор.

В Python, как в интерпретируемом ЯП с виртуальной машиной, память выделяет она. А «конструктором» класса в питоне, повторяюсь, будет метод __new__. Конструктор по определению, как минимум, должен возвращать инстанс класса. __new__ этому условию удовлетворяет, __init__ нет.

Конструктор получает уже готовую память. Т.е обьект, но в неизвестном состоянии. Его задача - привести обьект в корректное начальное состояние.

В питоне говорить про память… Ну-ну… Если у вас после __new__ объект пребывает в неизвестном состоянии, то вы очень оригинально проектируете свои типы данных. Назначение метода __init__ — манки-патчинг готового инстанса базового класса, чтобы не заниматься патчингом конструктора, поскольку таковой уровень ковыряния в кишках класса обычно без надобности.

Virtuos86 ★★★★★
()
Последнее исправление: Virtuos86 (всего исправлений: 1)
Ответ на: комментарий от Virtuos86

Назначение метода __init__ — манки-патчинг готового инстанса базового класса

Именно это я и имел ввиду. Просто, мы немного о разных вещах говорим. В моем определении конструтор - это скорее конструктор как в C++. А __new__ - переопределение new из C++. Потому как я понимаю, базово в Python __new__ выделяет память под обьект и на нем вызывает __init__, а уже после __new__ возвращает готовый обьект, который ассоциируется с именем.

crarkie
()
Ответ на: комментарий от crarkie

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

anonymous
()
Ответ на: комментарий от anonymous

Все языки во многом похожи. Большая разница лишь в парадигмах, и каких-то устоявшихся практиках написания кода. Другое дело, когда говорят об императивном и функциональном программировании, к примеру. Тут уже различий намного больше.

anonymous
()
Ответ на: комментарий от steemandlinux

что за данные вывел print(err)

Он вывел результат, который вернул вызов err.__str__(). Ты метод __str__ переопределял? Нет? Смотри в родительский класс.

i-rinat ★★★★★
()
Ответ на: комментарий от anonymous

Все языки во многом похожи

И во многом отличны. Иначе бы их не было. Питон и C++ отличаются сильно. Не стоит смотреть на один через призму второго.

Пишу и на том и на том, если что.

anonymous
()
Ответ на: комментарий от anonymous

Почему тогда если убрать Exception, то __init__ начинает себя вести нормально и выводиться адрес класса, а не строки?

steemandlinux ★★★★★
() автор топика
Ответ на: комментарий от steemandlinux

Почему тогда если убрать Exception, то __init__ начинает себя вести нормально и выводиться адрес класса, а не строки?

Потому что в BaseException переопределен __new__. А уже new вызывает внутри себя init и может подсмотреть его параметры.

anonymous
()
Ответ на: комментарий от steemandlinux

А вот уже понятно, у меня было подозрение, что там по умолчанию все строки печатаются.

Если вместо одного параметра сделать __init__(self, a, b, c), то выведется tuple из (a, b, c).

anonymous
()
Ответ на: комментарий от anonymous

Другое дело, когда говорят об императивном и функциональном программировании, к примеру. Тут уже различий намного больше.

Ну и люди реже начинают тянуть понятия из одного языка в другой. А тут многим кажется, что похожие вещи, потому пытаются сову на глобус натягивать.

anonymous
()
Ответ на: комментарий от steemandlinux
>>> ResponseKeyError.__str__
<slot wrapper '__str__' of 'BaseException' objects>
>>> 
static PyTypeObject _PyExc_BaseException = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "BaseException", /*tp_name*/
    sizeof(PyBaseExceptionObject), /*tp_basicsize*/

    /* <...> */

    (reprfunc)BaseException_str,  /*tp_str*/

    /* <...> */
static PyObject *
BaseException_str(PyBaseExceptionObject *self)
{
    switch (PyTuple_GET_SIZE(self->args)) {
    case 0:
        return PyUnicode_FromString("");
    case 1:
        return PyObject_Str(PyTuple_GET_ITEM(self->args, 0));
    default:
        return PyObject_Str(self->args);
    }
}
i-rinat ★★★★★
()
Ответ на: комментарий от steemandlinux

Функция не может не возвращать, но задача инита не возвращать, а изменять переданный в неё инстанс.

WitcherGeralt ★★
()
Ответ на: комментарий от crarkie

Ничего оно не сложнее, ты буквально можешь написать return None.

WitcherGeralt ★★
()
Ответ на: комментарий от i-rinat

У него был вопрос про то, откуда в ошибке берётся переданный в неё стринг при том, что инит пустой. Выше ему объяснили.

WitcherGeralt ★★
()
Последнее исправление: WitcherGeralt (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.