Эффективный способ определить, является ли конкретная функция на стеке в Python

Добавьте сам подписанный сертификат к Доверенным корневым центрам сертификации Локального компьютера

, можно импортировать сертификат путем выполнения MMC как, Администратор.

, Как к: Сертификаты Представления с Защелкой MMC - в

5
задан Community 23 May 2017 в 10:32
поделиться

2 ответа

Если функция, к которой вы стремитесь, не делает что-то особенное, чтобы отметить «один экземпляр меня активен в стеке» (IOW: если функция является нетронутой и неприкасаемой и не может быть знайте об этой вашей особой потребности), нет никакой мыслимой альтернативы переходу от кадра к кадру вверх по стеку, пока вы не достигнете вершины (а функции там нет) или кадра стека для интересующей вас функции. Как показывают несколько комментариев к вопросу, крайне сомнительно, что стоит стремиться к оптимизации. Но, предположив ради аргумента, что это было стоящим ...:

Изменить : исходный ответ (OP) содержал много дефектов, но некоторые с тех пор были исправлены, поэтому Я' функция может находиться в стеке несколько раз (прямая или косвенная рекурсия). Нам явно нужен «мульти-набор» (также известный как «мешок»), структура, подобная множеству, которая отслеживает, «сколько раз» каждый элемент присутствует. В Python естественная реализация мультимножества - это dict, отображающая ключи в счетчики, что, в свою очередь, наиболее удобно реализовать как collections.defaultdict (int) .

В-четвертых, общий подход должен быть threadsafe (по крайней мере, если это можно сделать легко ;-). К счастью, threading.local делает его тривиальным, когда это применимо - и здесь, безусловно, должно быть (каждый стек имеет свой собственный отдельный поток вызовов).

В-пятых, затронутый интересный вопрос в некоторых комментариях (замечая, насколько плохо предлагаемые декораторы в некоторых ответах играют с другими декораторами: декоратор мониторинга, по-видимому, должен быть ПОСЛЕДНИМ (самым внешним), иначе проверка прервется. Это происходит из-за естественного, но неудачного выбора использования самого функционального объекта в качестве ключа к мониторингу.

Я предлагаю решить эту проблему другим выбором ключа: заставить декоратор взять (скажем, строку) аргумент идентификатора , который должен быть уникальным (в каждом заданном потоке) и использовать идентификатор в качестве ключа в слове мониторинга. Код, проверяющий стек, конечно, должен знать идентификатор и также использовать его.

Во время декорирования декоратор может проверить свойство уникальности (используя отдельный набор). Идентификатор может быть оставлен по умолчанию для имени функции (так что это явно требуется только для сохранения гибкости мониторинга одноименных функций в том же пространстве имен); от свойства уникальности можно явным образом отказаться, когда несколько отслеживаемых функций должны считаться «одинаковыми» для целей мониторинга (это может иметь место, если данный оператор def предназначен для выполнения несколько раз в немного разных контекстах создать несколько функциональных объектов, которые программисты хотят рассматривать как «одну и ту же функцию» для целей мониторинга). Наконец, должна быть возможность при желании вернуться к «объекту функции как идентификатору» в тех редких случаях, когда дальнейшее оформление ИЗВЕСТНО, что невозможно (поскольку в таких случаях это может быть самый удобный способ гарантировать уникальность)

. объединив все эти соображения, мы могли бы иметь (включая служебную функцию threadlocal_var , которая, вероятно, уже будет в модуле панели инструментов, конечно; -) что-то вроде следующего ...:

import collections
import functools
import threading

threadlocal = threading.local()

def threadlocal_var(varname, factory, *a, **k):
  v = getattr(threadlocal, varname, None)
  if v is None:
    v = factory(*a, **k)
    setattr(threadlocal, varname, v)
  return v

def monitoring(identifier=None, unique=True, use_function=False):
  def inner(f):
    assert (not use_function) or (identifier is None)
    if identifier is None:
      if use_function:
        identifier = f
      else:
        identifier = f.__name__
    if unique:
      monitored = threadlocal_var('uniques', set)
      if identifier in monitored:
        raise ValueError('Duplicate monitoring identifier %r' % identifier)
      monitored.add(identifier)
    counts = threadlocal_var('counts', collections.defaultdict, int)
    @functools.wraps(f)
    def wrapper(*a, **k):
      counts[identifier] += 1
      try:
        return f(*a, **k)
      finally:
        counts[identifier] -= 1
    return wrapper
  return inner

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

Это все того стоит? Вероятно, нет, как объяснялось ранее. Однако я думаю, что «если это вообще стоит делать, то стоит делать правильно»; -).

12
ответ дан 18 December 2019 в 14:48
поделиться

Мне не очень нравится этот подход, но вот исправленная версия того, что вы делали:

from collections import defaultdict
import threading
functions_on_stack = threading.local()

def record_function_on_stack(f):
    def wrapped(*args, **kwargs):
        if not getattr(functions_on_stack, "stacks", None):
            functions_on_stack.stacks = defaultdict(int)
        functions_on_stack.stacks[wrapped] += 1

        try:
            result = f(*args, **kwargs)
        finally:
            functions_on_stack.stacks[wrapped] -= 1
            if functions_on_stack.stacks[wrapped] == 0:
                del functions_on_stack.stacks[wrapped]
        return result

    wrapped.orig_func = f
    return wrapped

def function_is_on_stack(f):
    return f in functions_on_stack.stacks

def nested():
    if function_is_on_stack(test):
        print "nested"

@record_function_on_stack
def test():
    nested()

test()

Это обрабатывает рекурсию, многопоточность и исключения.

Я не нравится этот подход по двум причинам:

  • Он не работает, если функция декорирована дальше: это должен быть последний декоратор.
  • Если вы используете это для отладки, это означает, что вам нужно отредактировать код в двух места для его использования; один для добавления декоратора и один для его использования. Гораздо удобнее просто исследовать стек, поэтому вам нужно только редактировать код в отлаживаемом коде.

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

1
ответ дан 18 December 2019 в 14:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: