Возможно ли напечатать имя переменной из списка [duplicate]

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

Проблема в том, что DLL и шаблоны не согласны вместе очень хорошо. В общем, в зависимости от привязки среды выполнения MSVC это может быть проблемой, если память выделена в исполняемом файле и освобождена в DLL и наоборот (потому что у них могут быть разные кучи). И это может произойти с шаблонами очень легко, например: вы push_back () в вектор внутри removeWhiteSpaces () в DLL, поэтому векторная память выделяется внутри DLL. Затем вы используете выходной вектор в исполняемом файле, и как только он выходит из области видимости, он освобождается, но внутри исполняемого файла, чья куча ничего не знает о куче, из которой она была выделена. Bang, вы мертвы.

Это можно обойти, если и DLL, и исполняемый файл используют одну и ту же кучу. Чтобы обеспечить это, DLL и исполняемый файл должны использовать динамическую среду выполнения MSVC, поэтому убедитесь, что обе они связаны с динамической динамикой, а не статически. В частности, exe должен быть скомпилирован и связан с / MD [d] и библиотекой с / LD [d] или / MD [d], а также с / MT [d]. Обратите внимание, что впоследствии компьютеру, на котором будет запущено приложение, потребуется запустить исполняемую библиотеку MSVC (например, путем установки «Распространяемого на Visual C ++» для конкретной версии MSVC).

Вы могли бы получить эту работу даже с / MT, но это сложнее - вам нужно будет предоставить некоторый интерфейс, который позволит также освободить объекты, выделенные в DLL. Например, что-то вроде:

__declspec(dllexport) void deallocVector(std::vector<std::string> &x);

void deallocVector(std::vector<std::string> &x) {
    std::vector<std::string> tmp;
    v.swap(tmp);
}

(однако это не очень хорошо работает во всех случаях, так как это нужно вызывать явно, поэтому он не будет называться, например, в случае исключения - для правильного решения , вам нужно будет предоставить некоторый интерфейс из DLL, который будет охватывать вектор под капотом и позаботится о правильном RAII)


EDIT: окончательное решение было фактически состоять в том, чтобы все проектов (exe, dll и весь проект googleTest), встроенных в многопоточную Debug DLL (/ MDd) (по умолчанию проекты GoogleTest встроены в многопоточную отладку (/ MTd))

7
задан Groady 16 January 2012 в 04:37
поделиться

5 ответов

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

class Foo(object):
    def __init__(self): pass

x = Foo()

два имени обозначают один и тот же объект (self, когда __init__ работает, x в глобальной области).

4
ответ дан Cat Plus Plus 20 August 2018 в 07:36
поделиться

Как и многие другие, это невозможно сделать должным образом. Однако, вдохновленный jsbueno's, у меня есть альтернатива его решению.

Как и его решение, я проверяю кадр стека вызывающих, что означает, что он работает только правильно для вызываемых Python абонентов (см. примечание ниже). В отличие от него, я проверяю байт-код вызывающего абонента напрямую (вместо загрузки и разбора исходного кода). Используя Python 3.4 + 's dis.get_instructions(), это можно сделать с некоторой надеждой на минимальную совместимость. Хотя это все еще какой-то хакерский код.

import inspect
import dis

def take1(iterator):
    try:
        return next(iterator)
    except StopIteration:
        raise Exception("missing bytecode instruction") from None

def take(iterator, count):
    for x in range(count):
        yield take1(iterator)

def get_assigned_name(frame):
    """Takes a frame and returns a description of the name(s) to which the
    currently executing CALL_FUNCTION instruction's value will be assigned.

    fn()                    => None
    a = fn()                => "a"
    a, b = fn()             => ("a", "b")
    a.a2.a3, b, c* = fn()   => ("a.a2.a3", "b", Ellipsis)
    """

    iterator = iter(dis.get_instructions(frame.f_code))
    for instr in iterator:
        if instr.offset == frame.f_lasti:
            break
    else:
        assert False, "bytecode instruction missing"
    assert instr.opname.startswith('CALL_')
    instr = take1(iterator)
    if instr.opname == 'POP_TOP':
        raise ValueError("not assigned to variable")
    return instr_dispatch(instr, iterator)

def instr_dispatch(instr, iterator):
    opname = instr.opname
    if (opname == 'STORE_FAST'              # (co_varnames)
            or opname == 'STORE_GLOBAL'     # (co_names)
            or opname == 'STORE_NAME'       # (co_names)
            or opname == 'STORE_DEREF'):    # (co_cellvars++co_freevars)
        return instr.argval
    if opname == 'UNPACK_SEQUENCE':
        return tuple(instr_dispatch(instr, iterator)
                     for instr in take(iterator, instr.arg))
    if opname == 'UNPACK_EX':
        return (*tuple(instr_dispatch(instr, iterator)
                     for instr in take(iterator, instr.arg)),
                Ellipsis)
    # Note: 'STORE_SUBSCR' and 'STORE_ATTR' should not be possible here.
    # `lhs = rhs` in Python will evaluate `lhs` after `rhs`.
    # Thus `x.attr = rhs` will first evalute `rhs` then load `a` and finally
    # `STORE_ATTR` with `attr` as instruction argument. `a` can be any 
    # complex expression, so full support for understanding what a
    # `STORE_ATTR` will target requires decoding the full range of expression-
    # related bytecode instructions. Even figuring out which `STORE_ATTR`
    # will use our return value requires non-trivial understanding of all
    # expression-related bytecode instructions.
    # Thus we limit ourselfs to loading a simply variable (of any kind)
    # and a arbitary number of LOAD_ATTR calls before the final STORE_ATTR.
    # We will represents simply a string like `my_var.loaded.loaded.assigned`
    if opname in {'LOAD_CONST', 'LOAD_DEREF', 'LOAD_FAST',
                    'LOAD_GLOBAL', 'LOAD_NAME'}:
        return instr.argval + "." + ".".join(
            instr_dispatch_for_load(instr, iterator))
    raise NotImplementedError("assignment could not be parsed: "
                              "instruction {} not understood"
                              .format(instr))

def instr_dispatch_for_load(instr, iterator):
    instr = take1(iterator)
    opname = instr.opname
    if opname == 'LOAD_ATTR':
        yield instr.argval
        yield from instr_dispatch_for_load(instr, iterator)
    elif opname == 'STORE_ATTR':
        yield instr.argval
    else:
        raise NotImplementedError("assignment could not be parsed: "
                                  "instruction {} not understood"
                                  .format(instr))

Примечание: C-реализованные функции не отображаются в виде фреймов стека Python и поэтому скрыты для этого скрипта. Это приведет к ложным срабатываниям. Рассмотрим функцию Python f(), которая вызывает a = g(). g() является C-реализованным и вызывает b = f2(). Когда f2() пытается найти назначенное имя, он получит a вместо b, потому что сценарий не обращает внимания на функции C. (По крайней мере, так я думаю, что это сработает: P)

Пример использования:

class MyItem():
    def __init__(self):
        self.name = get_assigned_name(inspect.currentframe().f_back)

abc = MyItem()
assert abc.name == "abc"
1
ответ дан driax 20 August 2018 в 07:36
поделиться

Это невозможно сделать обычно, хотя это может быть достигнуто с помощью интроспекции и средств, предназначенных для отладки программы. Код должен выполняться из файла «.py», хотя, а не только скомпилированного байт-кода или внутри заархивированного модуля, поскольку он полагается на чтение исходного кода файла, из метода, который должен найти «где это running ".

Фокус в том, чтобы получить доступ к кадру выполнения, в котором объект был инициализирован, - с помощью inspect.currentframe - объект фрейма имеет значение« f_lineno », в котором указывается номер строки, где вызов объекта метод (в данном случае __init__). Функция inspect.filename позволяет извлекать исходный код для файла и извлекать соответствующий номер строки.

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

from inspect import currentframe, getfile

class A(object):
    def __init__(self):
        f = currentframe(1)
        filename = getfile(f)
        code_line = open(filename).readlines()[f.f_lineno - 1] 
        assigned_variable = code_line.split("=")[0].strip()
        print assigned_variable

my_name = A()
other_name = A()

Это не будет работать для нескольких назначений, выражений, составляющих объект до создания assignemtn, объектов, добавляемых к спискам или добавляемых к словарям или наборам, экземпляра объекта в инциализации петель for, и Бог знает, что больше ситуаций - и имейте в виду, что после

Botton line: возможно , но как игрушка - он не может быть использован i производственный код - просто нужно, чтобы имя переменной было передано как строка во время инициализации объекта, так же, как это нужно делать при создании collections.namedtuple

. «Правильный путь» для этого, если вам нужно имя , заключается в том, чтобы явно передать имя инициализации объекта в качестве параметра строки, например, в:

class A(object):
  def __init__(self, name):
      self.name = name

x = A("x")

И все же, если абсолютно необходимо вводить имя объекта только один раз, есть еще один путь - читайте дальше. Из-за синтаксиса Python некоторые специальные назначения, не использующие оператор «=», позволяют объекту знать, что ему присвоено имя. Таким образом, другие statemtns, которые выполняют назначающие функции в Python, являются ключевыми словами for, with, def и class. Возможно, это злоупотребление, так как specfically создание класса и определение функции являются операторами присваивания, которые создают объекты, которые «знают» их имена.

Давайте сосредоточимся на утверждении def. Он обычно создает функцию. Но с помощью декоратора вы можете использовать «def» для создания любого объекта - и имя, используемое для функции, доступной для конструктора:

class MyObject(object):
   def __new__(cls, func):
       # Calls the superclass constructor and actually instantiates the object:
       self = object.__new__(cls)
       #retrieve the function name:
       self.name = func.func_name
       #returns an instance of this class, instead of a decorated function:
       return self
   def __init__(self, func):
       print "My name is ", self.name

#and the catch is that you can't use "=" to create this object, you have to do:

@MyObject
def my_name(): pass

(Последний способ сделать это можно использовать в производственном коде, в отличие от того, который прибегает к чтению исходного файла)

1
ответ дан jsbueno 20 August 2018 в 07:36
поделиться

Вот простая функция для достижения желаемого результата, предполагая, что вы хотите получить имя переменной, где экземпляр назначен из вызова метода:

import inspect

def get_instance_var_name(method_frame, instance):
    parent_frame = method_frame.f_back
    matches = {k: v for k,v in parent_frame.f_globals.items() if v is instance}
    assert len(matches) < 2
    return matches.keys()[0] if matches else None

Вот пример использования:

class Bar:
    def foo(self):
        print get_instance_var_name(inspect.currentframe(), self)

bar = Bar()
bar.foo()  # prints 'bar'

def nested():
    bar.foo()
nested()  # prints 'bar'

Bar().foo()  # prints None
0
ответ дан Lucas Cimon 20 August 2018 в 07:36
поделиться

Да, возможно *. Однако проблема сложнее, чем кажется на первый взгляд:

  • На один и тот же объект может быть несколько имен.
  • Не может быть никаких имен вообще.

Несмотря на это, знание способов поиска имен объекта иногда может быть полезно для целей отладки - и вот как это сделать:

import gc, inspect

def find_names(obj):
    frame = inspect.currentframe()
    for frame in iter(lambda: frame.f_back, None):
        frame.f_locals
    obj_names = []
    for referrer in gc.get_referrers(obj):
        if isinstance(referrer, dict):
            for k, v in referrer.items():
                if v is obj:
                    obj_names.append(k)
    return obj_names

Если вы «когда-либо соблазнялось основать логику вокруг имен ваших переменных, приостановить на мгновение и подумать, может ли редизайн / рефакторинг кода решить проблему. Необходимость восстановления имени объекта из самого объекта обычно означает, что базовые структуры данных в вашей программе нуждаются в переосмыслении.

*, по крайней мере, на Cpython

4
ответ дан wim 20 August 2018 в 07:36
поделиться
Другие вопросы по тегам:

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