Получение имени атрибута, которому будет присвоен созданный объект - PullRequest
1 голос
/ 09 апреля 2010

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

Это так. У меня есть объект из класса A, который создает несколько B экземпляров в качестве атрибутов:

class A(object):
    def __init__(self)
        self.vanilla_b = B()
        self.chocolate_b = B()

class B(object):
    def __init__(self):
        # ...

Я хочу, чтобы в B.__init__ он вычислял "vanilla_b" или любое другое имя атрибута, которое ему было дано, а затем помещал его как атрибут .name для этого конкретного B.

Затем при отладке, когда я вижу какой-то объект B, плавающий вокруг, я могу знать, какой это.

Есть ли способ сделать это?

Ответы [ 4 ]

2 голосов
/ 09 апреля 2010

Вы можете использовать sys._getframe , чтобы получить номер строки, где вызывается B(), затем вы можете использовать inspect.getsourcelines , чтобы получить фактическую строку кода. Оттуда вы можете разобрать строку кода, чтобы получить объект, которому присваивается B():

import sys
import inspect

class A(object):
    def __init__(self):
        self.vanilla_b = B()
        self.chocolate_b = B()

class B(object):
    def __init__(self):
        line_num = sys._getframe().f_back.f_lineno
        lines = inspect.getsourcelines( sys.modules[__name__] )[0]
        line = lines[line_num - 1]
        attr = line.split("=")[0].split(".")[1].strip()
        print "B() is being assigned to", attr

A()

Если вы поместите приведенный выше код в скрипт Python и запустите его, он напечатает

B() is being assigned to vanilla_b
B() is being assigned to chocolate_b

Однако, это не будет работать в командной строке Python, так как __main__ является встроенным модулем, и, таким образом, модуль inspect не может извлечь свои строки исходного текста. Так что вы можете захотеть обернуть это в блок try / catch или что-то еще на случай, если ваш код когда-либо будет вызван из любого встроенного модуля.

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

0 голосов
/ 09 апреля 2010

Не очень волшебно, но:

class A(object):
    def __init__(self)
        self.vanilla_b = B(self)
        self.chocolate_b = B(self)

и:

class B(object):
    def __init__(self, a):
        for i in dir(a):
            if getattr(a, i) == self:
                pass # store it somewhere now

РЕДАКТИРОВАТЬ: Извините, это не будет работать.B. init выполняется до того, как заданы ссылки в A.

0 голосов
/ 09 апреля 2010

Если у вас есть контроль над кодом, то лучше сделать это, а затем полагаться на какую-то чёрную магию, например делать

class A(object):
    def __init__(self)
        self.vanilla_b = B(name="vanilla_b")
        self.chocolate_b = B(name="chocolate_b")

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

0 голосов
/ 09 апреля 2010

Возможно, это не тот ответ, который вы ищете, но, возможно, вы могли бы что-то сделать в этом направлении:

class A(object):
    def __init__(self):
        attrs = ('vanilla_b', 'chocolate_b')

        for attr in attrs:
            instance = B()
            setattr(self, attr, instance)
            instance.name = attr

Попробуйте перенести это поведение в какой-нибудь суперкласс, и вам, возможно, будет хорошо.

...