如何从其中一个属性的值中检索类的实例?

时间:2018-04-13 10:02:49

标签: python

我想(优雅地)序列化不同类实例中属性之间的连接。例如:

class AttrClass(object):
    def __init__(self, value=None):
        self.value = value

class ObjClass(object):
    def __init__(self, name, value=None):
        self.name = name
        self.foo = AttrClass(value=value)

out_obj = ObjClass('out', value='bar')
in_obj = ObjClass('in', value=out_obj.foo)
in_obj.foo
# Result: <AttrClass object at 0x000001B086B0A470>

在这种情况下,我还想找到ObjClass类的out_obj实例和属性名foo。如果self'foo'被传递到AttrClass初始化,那么可以这样做,但是每次用户创建一个以该类为值的新属性时,用户都会继续输入它。 p>

有没有一种方法可以追溯一个值到属性名称,它是值和属性所属的类实例?

或者,有没有一种方法可以在实例化时自动将类实例和属性名称添加到类中?例如:

class AttrClass(object):
    def __init__(self, value=None):
        self.value = value
        self.host_class = self.do_magic_class_thing()
        self.host_attr = self.do_magic_attr_thing()

out_obj = ObjClass('out', value='bar')
in_obj = ObjClass('in', value=out_obj.foo)
in_obj.foo.host_class.name
# Result: 'out'

2 个答案:

答案 0 :(得分:0)

你不能,至少不是优雅 - 你可以用很多inspect魔法做到这一点,但它会变得非常丑陋很快。这个问题&#39;存在于几乎所有语言中,依赖于他们的解释器/ VM来通过引用计数进行内存管理 - 如果对象的子项要存储对父母的引用,则会得到一个不能快速解决的循环引用计数,并且因为语言设计师通常会选择不这样做,而是让开发人员处理他们自己的特定情况。

因此,在您的特定情况下,由于您正在包装您的值AttrClass实例,您可以让这些实例保持对其父母的引用:

class AttrClass(object):
    def __init__(self, parent, value=None):
        self.parent = parent
        self.value = value

class ObjClass(object):
    def __init__(self, name, value=None):
        self.name = name
        self.foo = AttrClass(self, value=value)

out_obj = ObjClass('out', value='bar')
in_obj = ObjClass('in', value=out_obj.foo)

assert in_obj.foo.value.parent is out_obj  # passes
print(in_obj.foo.value.parent.name)  # out

另外,为什么需要编写自己的序列化器/反序列化器有什么特别的原因吗?那里有很多这样的东西,我确信至少有一些适合你的用例。我全心全意地推荐MessagePack

答案 1 :(得分:0)

你去吧。此代码没有任何保证,因为它依赖于解释器实现中的Python堆栈帧支持。

import inspect, re


class AttrClass(object):
    def __init__(self, value=None):
        self.value = value

        stack = inspect.stack()     # get the stack
        goback = stack[2]           # go down the stack 2 step
        entry_point = goback[4]     # get the code context to get "object name" [4] is from stack format

        name = re.findall("ObjClass\(.*value=([^)]*)\)", entry_point[0])  # magic hack to get "out_obj"
        if len(name) > 0:
            obj_name = name[0].split('.')[0]    # get the object's name.

            argvalues = inspect.getargvalues(goback.frame)[3]       # [3] to get `locals` from tuple returned.

            if obj_name in argvalues:
                setattr(self, 'host_class', argvalues[obj_name])    # iject the attribute

        else:   #Sorry can't find it
            setattr(self, 'host_class', None)


class ObjClass(object):
    def __init__(self, name, value=None):
        self.name = name
        self.foo = AttrClass(value=value)


out_obj = ObjClass('out', value='bar')
in_obj = ObjClass('in', value=out_obj.foo)

print(in_obj.foo.host_class.name)