Python return and finally

Python: Is it possible to access the return value inside the `finally` clause?

Is it possible to get the return value inside the finally clause? This is more of a theoretical question, so I’m not looking for a workaround like saving it to a variable.

5 Answers 5

No, it isn’t — the finally clause’s content is independent from return mechanics; if you do want to see what value is returned you’d have to do as you mentioned and explicitly save it somewhere that’s in-scope.

How certain are you of this? Are you familiar with the internal mechanics of how Python handles this?

Pretty certain — there’s no way to get at it from Python, at least. You might be able to hack around with stuff via C, but the closest you get to this info in Python is inspect and you’ll notice there’s no listing of return value information there.

@RamRachum: this also holds true for Java and is a classical mechanism of try//finally . To give a concrete example, a common idiom is for a method to throw an exception because it could not read the content of a stream, all the while closing the stream in the finally block; only if the try block completes successfully is a result returned, otherwise an exception is thrown.

Do you absolutely have to «get in» after the return statement?

If changes allowed before the return statement, sys.settrace() is all you need.

Getting the value after return :

I think, in stackless Python, you should be able to do that. «threads» can be pickled in stackless, and about-to-be-returned value, aka top of value stack, ought to be there.

In CPython, I couldn’t find a way peek at top of value stack yet.

  • dynamically changing frame.f_lasti is not allowed
  • dynamically changing frame.f_code is not allowed
  • dynamically changing frame.f_trace is allowed but doesn’t seem to help
  • set tracing function from within finally block doesn’t catch actual return event after return statement was «already executed»
  • with statment doesn’t catch return value
  • I assume caller ignores f’s return value, thus introspection or tracing the caller doesn’t help
  • I assume whatever() has side effects and cannot be called again
  • debuggers, at least those that I tried, don’t get return value (?); debuggers written in Python use sys.settrace and/or last exception, neither of these contains return value on stack.
Читайте также:  Javascript if inside if you

Of course, everything is possible with ctypes or C-level extension, here’s a quick demo:

""" valuestack.py: Demo reading values from Python value stack Offsets are derived for CPython-2.7.2, 64-bit, production build """ import ctypes, inspect, random def id2obj(i): """convert CPython `id` back to object ref, by temporary pointer swap""" tmp = None, try: ctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i return tmp[0] finally: ctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None) def introspect(): """pointer on top of value stack is id of the object about to be returned FIXME adjust for sum(vars, locals) in introspected function """ fr = inspect.stack()[1][0] print "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47]) def value(): tmp = random.random() print "return", tmp return tmp def foo(): try: return value() finally: introspect() if __name__ == "__main__": foo() 

Works with Python-2.7.2 in 64-bit mode as shipped with osx:

air:~ dima$ python valuestack.py return 0.959725159294 caught 0.959725159294 

Источник

Предостережения при использовании return с try / за исключением Python

Код пользователя может вызывать встроенные исключения. Python определяет try / except для обработки исключений и продолжения дальнейшего выполнения программы без прерывания.

Давайте быстро перейдем к примеру простого предложения try / except.

try / except операторы

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

try: f = open("testfile.txt") . except FileNotFoundError as e: print(f" Error while reading file ") Output: Error while reading file [Errno 2] No such file or directory: 'testfile.txt'

В практических случаях использования, таких как подключение к базе данных или открытие файлового объекта, нам может потребоваться выполнить операции разрыва, такие как закрытие базы данных / закрытие файла, независимо от того, выполняется ли блок. Итак, finally — один из таких блоков, который можно зарезервировать для этих операций, поскольку он всегда выполняется. Давайте посмотрим на пример.

операторы try / except / finally

try: f = open("testfile.txt") except FileNotFoundError as e: print(f" Error while reading file ") finally: print(" Closing the file ") f.close()

Так что же здесь могло пойти не так? Почему мы должны быть осторожными?

Что ж, можно легко поставить ногу им в рот, когда они используют операторы return с try / except / finally в Python. Давайте внимательно сделаем шаг за шагом, чтобы понять использование операторов return во время обработки исключений.

1. Использование return с try / except

def test_func(): try: x = 10 return x except Exception as e: x = 20 return x finally: x = 30 return x print(test_func()) Output: 30

Если вы думаете, что вывод приведенного выше кода равен 10, боюсь, вы ошибаетесь. Это предположение вполне нормально, потому что мы склонны думать, что в тот момент, когда в функции есть оператор return, он возвращает (выходит) из функции. Что ж, в данном случае это может быть неверно.

  • Если присутствует предложение finally , предложение finally будет выполняться как последняя задача перед завершением оператора try . Предложение finally выполняется независимо от того, вызывает ли оператор try исключение.
  • Если оператор try достигает оператора break , continue или return , предложение finally будет выполняться непосредственно перед выполнением оператора break , continue или return .
  • Если предложение finally включает оператор return , возвращаемое значение будет значением из оператора return предложения finally , а не значением из оператора return предложения try/except .
Читайте также:  Pdf gray hat python

Итак, как вы правильно догадались, на выходе приведенного выше кода будет 30.

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

2. Использование возврата с исключениями

def test_func(): try: x = 10 raise Exception except Exception as e: print(f" Raising exception ") x = 20 return x finally: x = 30 return x print(test_func()) Output: Raising exception 30

Итак, снова выходным значением x будет 30. Мы должны помнить тот факт, что оператор finally выполняется ВСЕГДА.

Чтобы иметь более четкое представление о потоке выполнения, давайте добавим операторы печати в каждый блок.

def test_func(): try: x = 10 print(f" Inside try block ") return x except Exception as e: x = 20 print(f" Inside except block ") return x finally: x = 30 print(f" Inside finally block ") return x print(test_func()) Output: Inside try block Inside finally block 30

Это дало бы представление о потоке выполнения. Теперь, когда мы хорошо понимаем, как try / except / finally работает с операторами return, давайте попробуем втиснуть другое предложение.

Предложение else может быть добавлено вместе с try / except, и предложение else будет выполнено, если блок try не вызовет исключение.

3. Использование return с помощью try / else / finally

def test_func(): try: x = 10 print(f" Inside try block ") return x except Exception as e: x = 20 print(f" Inside except block ") return x else: print(f" Inside else block ") x = 40 return x finally: x = 30 print(f" Inside finally block ") return x print(test_func()) Output: Inside try block Inside finally block 30

Итак, почему здесь не было выполнено предложение else, хотя блок try не вызвал никаких исключений. Обратите внимание на оператор return в блоке try . Блок else никогда не выполнялся, потому что функция вернулась еще до того, как выполнение достигло предложения else.

Теперь удалите оператор return в блоке try и снова выполните приведенный выше код.

def test_func(): try: x = 10 print(f" Inside try block ") except Exception as e: x = 20 print(f" Inside except block ") return x else: print(f" Inside else block ") x = 40 return x finally: x = 30 print(f" Inside finally block ") return x print(test_func()) Output: Inside try block Inside else block Inside finally block 30
  • Будьте особенно осторожны при добавлении return в предложениях try / except / finally.
  • Предложение finally выполняется независимо от того, вызывает ли оператор try исключение.
  • Если предложение finally включает оператор return , возвращаемое значение будет значением из оператора return предложения finally .
  • Предложение else будет выполнено, если блок try не вызовет исключение.
Читайте также:  Idea настроить версию java

Python на простом английском

Вы знали, что у нас есть три публикации и канал на YouTube? Найдите ссылки на все на plainenglish.io!

Источник

Try-Finally: Get current returning value

After seeing this I saw the answers here and here which presented the reason why, and that’s that the finally clause will always be executed, no exceptions. My question is, where is the previous return value stored:

def t(): try: return True finally: . 

Why doesn’t this return None but instead returns the original True ? And is it possible to access the going to be returned value programatically?

def t(): try: return True finally: . # if returning != None: return False 
def t(): retval = None try: retval = "Set" finally: if retval != None: return retval else: return "Not Set" 
def t(): retval = None try: . finally: if retval != None: return retval else: return "Not Set" 

Obviously it’s stored somewhere so it can be used after the finally: clause (assuming that doesn’t contain a return statement), but that’s an implementation detail.

1 Answer 1

Why doesn’t this return None but instead returns the original True ?

The finally clause is also executed “on the way out” when any other clause of the try statement is left via a break , continue or return statement.

So this means that if a return statement is present in the finally clause that value will be returned (since the finally block is guaranteed to execute fully, any return statement inside it will be executed as well). Otherwise, if the try/except block was going to return a value, that value is returned.

def t(): try: print("Inside try") return "Return from try" finally: print("Inside finally") return "Return from finally" 
def t(): try: print("Inside try") # start of old finally block, executed "on the way out" print("Inside finally") return "Return from finally" # end of old finally block, inserted right before return statement in try return "Return from try" finally: pass 

And is it possible to access the going to be returned value programatically?

No, you can’t access the value after the return statement in other parts of the code without saving it.

Источник

Оцените статью