获取从对象内部分配对象的变量?

时间:2017-03-21 14:43:02

标签: python python-3.x class introspection

在Python中,我想获得一个对象被分配到的变量。类似的东西:

class Parent(object):

    def __init__(self):
        print(???) # Print the variable to which the created instance of 
                   # Parent has been assigned to. 

p = Parent() # This should print 'p'. 

我想出了这个:

import inspect


class Parent(object):

    def __init__(self):
        print((inspect.stack()[-1].code_context[-1].split()[0]))


p = Parent()

如果我理解正确的话,从字面上看最外面的调用,然后是左字符串部分,这恰好是变量 - 但对我来说这看起来很奇怪。有没有更好的办法?

3 个答案:

答案 0 :(得分:1)

您无法在__init__方法(以常规方式) 1 中获取实例名称。因为在__init__创建实例之后调用的__new__()方法然后,python将创建的对象返回给调用者。这意味着python将变量名映射到全局名称空间中的对象。

然而,解决此问题的一种方法是为您的对象定义另一种方法,以便在初始化后获取实例名称。您可以使用globals()内置函数来实现此目标:

In [5]: class Parent:
            def __init__(self):
                pass
            def get_instance_names(self):
                names = [i for i,j in globals().items() if isinstance(j, type(self))]
                if names:
                    return names
   ...:         

DEMO:

In [6]: a = Parent()

In [7]: a.get_instance_names()
Out[7]: ['a']

In [8]: a = b = c = Parent()

In [9]: a.get_instance_names()
Out[9]: ['c', 'a', 'b']

<子> 1.实际上取决于您正在执行代码的环境,它可能会有所不同,但您可以通过解析源代码的执行行来提取变量,也可以在较低级别提取变量,例如查看堆栈但它本身并不是一种正确的保存方式,而你可以简单地从全局名称空间的上层获取名称。

答案 1 :(得分:1)

是的,可以这样做,使用inspectast解析您可以使用inspect.stack()找到的源代码。

请注意,您的解决方案为我打印"__main__",,我在Python 3.5.2上进行测试,因此其他Python版本的答案可能略有不同。

import inspect
import ast

class Parent(object):

    def __init__(self):
        frame = inspect.stack()[1]
        # Parse python syntax of the assignment line
        st = ast.parse(frame.code_context[0].strip())
        stmt = st.body[0]
        # Assume class being instanced as simple assign statement
        assert(isinstance(stmt, ast.Assign))
        # Parse the target the class is assigned to
        target = stmt.targets[0]
        print(target.id) # prints "p"

p = Parent()

这仅适用于简单的assign语句。如果你有一个更复杂的assign语句,比如p, n = Parent(), 0,你需要添加更多的步骤来解析AST。原则上,可以解析涉及Parent()的所有有效语句,但在某些情况下不涉及任务(例如Parser())。

上面列出的代码因此只能作为如何解决问题的示例,而不是完整的解决方案。

答案 2 :(得分:1)

由于提出的各种原因,这通常是不可行的。在__init__或其他类初始化方法中,这是双重不可行的,因为对象尚未就绪 - 只有在初始化完成时,新对象才会返回到调用上下文并且(可能)分配给变量

但在Python中,正如您所看到的,“不可行”与“不可能”不同 - 您和一些答案有办法找到它,包括解析源代码文件,找到分配的位置,因此,猜猜名字。

你没有告诉为什么你想要这个,但这看起来主要是无用的我能想到的任何东西。分配对象的变量在运行时几乎没用 - 无论哪种方式,它都是已知的。

所以,如果你检查一下,在stdlib本身你有了namedtuple,当然还有类创建本身,这两者都要求在创建时明确地传递名称:

myclass = type("myclass", (object, ), {})
mytuple = collections.namedtuple("mytuple", "a b c")

如果你知道之后这个名称已经创建并分配了对象,那么有些方法可以通过检查调用框架或使用垃圾收集机制来检索它 - 它们会有点比你的黑客更好,因为他们不依赖于可用的源代码文件。

但他们再一次,我建议在“生产”代码中反对它。简而言之,使用“gc”的方式是调用“gc.get_referrers”。您可以将“名称查找”放在方法中,甚至可以将属性放在某个属性中:

import gc
class Parent(object):
    @property
    def get_names(self):
        names = []
        for referrer in gc.get_referrers(self):
            if not isinstance(referrer, dict):
                continue
            module = referrer.get("__name__", "")
            for key, value in referrer.items():
                if value is self:
                    names.append(".".join((module, key)))
        return names

另一种方法是检查调用者代码框中的全局字典,但是,在调用get_names的代码中,您将获得“self”的名称,而不是实例的定义。 / p>

但同样,我曾经只想要类似于类属性的东西 - 那么你有三种可能的方法将名称作为属性分配给Parent本身的实例:在元类{{1} }或__new__方法,以及从Python 3.6开始,在包含“Parent”实例的类的基类作为属性__init__方法,或者,如果您的__init_subclass__类是描述符(也就是说,确实有一个Parent方法),在这种情况下,也可以从Python 3.6开始,它可以有一个__get__方法,它将在(容器)类创建时调用。