从对象内部获取对象分配给的变量?
Get variable to which an object is assigned from inside the object?
在 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()
如果我理解正确的话,从字面上看是最外层的调用,那里有左边的字符串部分,它恰好是变量——但对我来说这看起来很奇怪。有没有更好的办法?
您无法在 __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
...:
演示:
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. 实际上取决于您在其中执行代码的环境,它可能会有所不同,但是您可以通过解析源代码的执行行或者可能在较低级别(例如查看堆栈)来提取变量,但是这根本不是一种合适的保存方式,而您可以简单地从全局名称空间等上层获取名称。
是的,你可以这样做,同时使用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()
这只适用于简单的赋值语句。如果您有更复杂的赋值语句,比如 p, n = Parent(), 0
,您将需要添加更多步骤来解析 AST。原则上,所有涉及 Parent()
的有效语句都可以被解析,但在某些情况下不涉及赋值(例如 Parser()
)。
因此上面列出的代码只能作为解决问题的示例,而不是完整的解决方案。
由于各种原因,这通常是不可行的。它在 __init__
或其他 class 初始化方法中是双重不可行的,因为对象尚未准备好 - 只有当初始化完成时,新对象才会返回到调用上下文并且(可能) 分配给一个变量。
但是在 Python 中,如您所见,"unfeasible" 与 "impossible" 不同 - 您和一些答案可以通过解析源代码文件找到它, 找到作业发生的位置,从而猜出名字。
你没有告诉为什么你想要这个,但是对于我能想到的任何东西,这看起来大部分没用。分配给对象的变量在运行时也很少用到 - 无论哪种方式,它都是已知的。
因此,如果您检查一下,在 stdlib 本身中您有 namedtuple,当然还有 class 创建本身,两者都需要在创建时显式传递名称:
myclass = type("myclass", (object, ), {})
mytuple = collections.namedtuple("mytuple", "a b c")
如果您可以在创建并分配对象后知道名称,则可以通过检查调用帧或使用垃圾收集机制来检索它的方法 -它们会比您的 hack 好一点,因为它们不依赖于可用的源代码文件。
但是,我再次建议他们不要在 "production" 代码中这样做。顺便提一下,使用 "gc" 的方法是调用 "gc.get_referrers"。你可以把你的 "name finding" 放在一个方法中,甚至是 属性 做一些事情:
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" 的名称,而不是实例所在的位置已定义。
但是,我曾经只想要 class 属性这样的东西 - 那么您可以通过三种可能的方式将名称分配为 Parent
实例本身的属性:在metaclass __new__
或 __init__
方法,并且从 Python 3.6 开始,在包含 classes 的基础 class 包含 "Parent" 作为属性 __init_subclass__
方法,或者,如果您的 Parent
class 是一个描述符(即,确实有一个 __get__
方法),在这种情况下,也来自Python 3.6,它可以有一个 __set_name__
方法,将在(容器)class 创建时调用。
在 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()
如果我理解正确的话,从字面上看是最外层的调用,那里有左边的字符串部分,它恰好是变量——但对我来说这看起来很奇怪。有没有更好的办法?
您无法在 __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
...:
演示:
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. 实际上取决于您在其中执行代码的环境,它可能会有所不同,但是您可以通过解析源代码的执行行或者可能在较低级别(例如查看堆栈)来提取变量,但是这根本不是一种合适的保存方式,而您可以简单地从全局名称空间等上层获取名称。
是的,你可以这样做,同时使用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()
这只适用于简单的赋值语句。如果您有更复杂的赋值语句,比如 p, n = Parent(), 0
,您将需要添加更多步骤来解析 AST。原则上,所有涉及 Parent()
的有效语句都可以被解析,但在某些情况下不涉及赋值(例如 Parser()
)。
因此上面列出的代码只能作为解决问题的示例,而不是完整的解决方案。
由于各种原因,这通常是不可行的。它在 __init__
或其他 class 初始化方法中是双重不可行的,因为对象尚未准备好 - 只有当初始化完成时,新对象才会返回到调用上下文并且(可能) 分配给一个变量。
但是在 Python 中,如您所见,"unfeasible" 与 "impossible" 不同 - 您和一些答案可以通过解析源代码文件找到它, 找到作业发生的位置,从而猜出名字。
你没有告诉为什么你想要这个,但是对于我能想到的任何东西,这看起来大部分没用。分配给对象的变量在运行时也很少用到 - 无论哪种方式,它都是已知的。
因此,如果您检查一下,在 stdlib 本身中您有 namedtuple,当然还有 class 创建本身,两者都需要在创建时显式传递名称:
myclass = type("myclass", (object, ), {})
mytuple = collections.namedtuple("mytuple", "a b c")
如果您可以在创建并分配对象后知道名称,则可以通过检查调用帧或使用垃圾收集机制来检索它的方法 -它们会比您的 hack 好一点,因为它们不依赖于可用的源代码文件。
但是,我再次建议他们不要在 "production" 代码中这样做。顺便提一下,使用 "gc" 的方法是调用 "gc.get_referrers"。你可以把你的 "name finding" 放在一个方法中,甚至是 属性 做一些事情:
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" 的名称,而不是实例所在的位置已定义。
但是,我曾经只想要 class 属性这样的东西 - 那么您可以通过三种可能的方式将名称分配为 Parent
实例本身的属性:在metaclass __new__
或 __init__
方法,并且从 Python 3.6 开始,在包含 classes 的基础 class 包含 "Parent" 作为属性 __init_subclass__
方法,或者,如果您的 Parent
class 是一个描述符(即,确实有一个 __get__
方法),在这种情况下,也来自Python 3.6,它可以有一个 __set_name__
方法,将在(容器)class 创建时调用。