在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()
如果我理解正确的话,从字面上看最外面的调用,然后是左字符串部分,这恰好是变量 - 但对我来说这看起来很奇怪。有没有更好的办法?
答案 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)
是的,可以这样做,使用inspect
和ast
解析您可以使用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__
方法,它将在(容器)类创建时调用。