В Python приведет ли возведение исключения через некоторое время к l oop к утечкам памяти? - PullRequest
1 голос
/ 28 февраля 2020

У меня есть следующий фрагмент кода, который заставил меня задуматься

# Utlizing api_sess from the /token response  
# api_sess is a requests.Session() with Authorization header set with session token

products_resp = api_sess.get(
    f'{API_BASE}/products',
    params = {'type': 'Item'}
)

if products_resp.ok:
    while True:
        current_page = products_resp.headers.get('X-Current-Page', 1)
        total_pages = products_resp.headers.get('X-Total-Pages', 0)
        products_json = products_resp.json()
        for product in products_json:
            print(product['groupCode'], product['sku'], product['productStatus'])
        # Handle pagination
        if current_page < total_pages:
            products_resp = api_sess.get(
                f'{API_BASE}/products',
                params = {'type': 'Item', 'page': current_page + 1}
            )
            if not products_resp.ok:
                # Break out of while loop before raising error
                break
        else:
            break
    # Raise exception outside of while loop to prevent memory leak
    if not products_resp.ok:
        raise ValueException(f'Request Error {products_resp.status_code}')
else:
    raise ValueException(f'Request Error {products_resp.status_code}')

Изначально я собирался raise исключение внутри while, где я проверяю ответ на запрос следующей страницы, вот так :

if current_page < total_pages:
    products_resp = api_sess.get(
        f'{API_BASE}/products',
        params = {'type': 'Item', 'page': current_page + 1}
    )
    if not products_resp.ok:
        raise ValueException(f'Request Error {products_resp.status_code}')
else:
    break

Сначала я ничего не думал об этом, но я начал задаваться вопросом, произойдет ли очистка ресурса, так как ошибка возникает внутри бесконечной while l oop из-за True состояние. Таким образом, почему я неизбежно переработал свою логику c, чтобы сначала вырваться из while l oop, а затем перепроверить статус ответа.

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

Безопасно ли удерживать повышение исключения внутри while l oop или это приведет к утечкам памяти?

Я знаю, что вопрос и сценарий могут показаться тривиальными, но я бы хотел по возможности придерживаться лучших практик во всех аспектах. И после моего поиска в Google (хотя я не тратил часы, по крайней мере) в этом вопросе не оказалось никаких применимых объяснений того, как этот сценарий обрабатывается.

1 Ответ

3 голосов
/ 28 февраля 2020

Независимо от того, покинете ли вы текущую область с помощью return или необработанное исключение, количество ссылок на объект будет уменьшено. Простой пример:

import sys

a = "hello"

def foo(b):
    x = a
    print(f'x: {sys.getrefcount(x)}')
    if b:
        return
    else:
        raise Exception()

print(f'a: {sys.getrefcount(a)}')
foo(True)
print(f'a: {sys.getrefcount(a)}')

try:
    print(f'a: {sys.getrefcount(a)}')
    foo(False)
except Exception:
    pass
print(f'a: {sys.getrefcount(a)}')

Точный вывод может отличаться, но вы должны наблюдать, что счетчик ссылок для x на единицу больше, чем счетчик ссылок для a, который остается постоянным до и после вызовов в foo.

В обоих случаях счетчик ссылок для глобального a увеличивается при вызове foo и уменьшается после выхода foo (или после оператора try, который перехватывает исключение завершается). Если исключение никогда не обнаруживается, интерпретатор все равно завершается, что вызывает вопрос утечки памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...