优雅的方法来解析包含使用Python中的join()生成的列表的字符串

时间:2016-12-19 17:39:55

标签: python parsing

我正在尝试找到一种生成对象的一般方法,这些对象可以使用parse模块转换为字符串然后再返回。例如,对于其实例只有两个属性-StringyObject的类a

b

脚本连续调用import parse class StringyObject(object): fmt = "{a} {b}" def __init__(self, a, b): self.a = a self.b = b def __str__(self): return self.fmt.format(a=self.a, b=self.b) @classmethod def parse(cls, string): result = parse.parse(cls.fmt, string) kwargs = result.named return cls(**kwargs) def __eq__(self, other): if isinstance(other, self.__class__): return self.__dict__ == other.__dict__ else: return NotImplemented if __name__ == "__main__": obj = StringyObject("foo", "bar") reconstructed_obj = StringyObject.parse(str(obj)) assert reconstructed_obj == obj, "The reconstructed object should be equivalent to the original one." 实例方法和__str__类方法,并验证生成的对象parseobj是否等效(此处定义为实例同一个班级,并有相同的词典;参见Elegant ways to support equivalence ("equality") in Python classes)。

到目前为止,这很好,但我想将此方法扩展到属性,这些属性是可变长度列表。例如,如果reconstructed_obj是一个列表,那么我可以执行以下操作:

b

这仍适用于此示例,但不太优雅,因为我开始必须使用import parse class StringyObject(object): fmt = "{a} {b}" separator = ", " def __init__(self, a, b): self.a = a assert isinstance(b, list), "b should be a list." self.b = b def __str__(self): b_string = self.separator.join(self.b) return self.fmt.format(a=self.a, b=b_string) @classmethod def parse(cls, string): result = parse.parse(cls.fmt, string) kwargs = result.named kwargs['b'] = kwargs['b'].split(cls.separator) return cls(**kwargs) def __eq__(self, other): if isinstance(other, self.__class__): return self.__dict__ == other.__dict__ else: return NotImplemented if __name__ == "__main__": obj = StringyObject("foo", ["bar1", "bar2"]) reconstructed_obj = StringyObject.parse(str(obj)) assert reconstructed_obj == obj, "The reconstructed object should be equivalent to the original object." join(),这是我想要使用split()避免的。此外,如果我在字符串表示中添加parse.parse之后的另一个属性c,则解析会变得混乱:

b

然后运行脚本

class StringyObject(object):
    fmt = "{a} {b} {c}"
    separator = ", "

    def __init__(self, a, b, c):
        self.a = a
        assert isinstance(b, list), "b should be a list."
        self.b = b
        self.c = c

    def __str__(self):
        b_string = self.separator.join(self.b)
        return self.fmt.format(a=self.a, b=b_string, c=self.c)

生成错误的obj = StringyObject("foo", ["bar1", "bar2"], "hello") result = parse.parse(StringyObject.fmt, str(obj)) 对象:

Result

我真正想要的是为<Result () {'a': 'foo', 'c': 'bar2 hello', 'b': 'bar1,'}> 实现一种“子解析器”,只要它能找到b就会继续运行,然后才继续解析separator 1}}。是否有一种优雅的方式来做到这一点?

2 个答案:

答案 0 :(得分:1)

我的建议是考虑使用ast.literal_eval。这个函数是Python文字结构的安全eval(int,float,strings,lists,dicts ......)

我无法使用parse库让您的示例正常工作,但如果您稍微修改格式字符串,则ast.literal_eval可以很轻松地使用它:

import ast

class StringyObject(object):
    fmt = "{a!r}, {b!r}"

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __str__(self):
        return self.fmt.format(a=self.a, b=self.b)

    @classmethod
    def parse(cls, string):
        return cls(*ast.literal_eval(string))

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.__dict__ == other.__dict__
        else:
            return NotImplemented

if __name__ == "__main__":
    objects = [("foo", "bar"), 
        ("foo", ["bar1", "bar2"]), 
        (["foo1", ("foo2", ["foo3", {"foo4"}])], {"bar1" : "bar2", "bar3": ["bar4", "bar5"]})]
    for a, b in objects:
        obj = StringyObject(a, b)
        reconstructed_obj = StringyObject.parse(str(obj))
        assert reconstructed_obj == obj, "The reconstructed object should be equivalent to the original one."

这个实现的缺点是它只适用于基本的python文字;即,StringyObject(frozenset(['foo']), 'bar')将无效。

答案 1 :(得分:0)

我发现可以通过在格式字符串中添加一些“固定”字符(而不仅仅是空格)来实现所需的解析结果。例如,下面我在./lib|之间放了一个管道{b}

{c}

打印的import parse class StringyObject(object): fmt = "{a} {b} | {c}" separator = ", " def __init__(self, a, b, c): self.a = a assert isinstance(b, list), "b should be a list." self.b = b self.c = c def __str__(self): b_string = self.separator.join(self.b) return self.fmt.format(a=self.a, b=b_string, c=self.c) @classmethod def parse(cls, string): result = parse.parse(cls.fmt, string) kwargs = result.named kwargs['b'] = kwargs['b'].split(cls.separator) return cls(**kwargs) def __eq__(self, other): if isinstance(other, self.__class__): return self.__dict__ == other.__dict__ else: return NotImplemented if __name__ == "__main__": obj = StringyObject("foo", ["bar1", "bar2"], "hello") result = parse.parse(StringyObject.fmt, str(obj)) print result reconstructed_obj = StringyObject.parse(str(obj)) assert reconstructed_obj == obj, "The reconstructed object should be equivalent to the original object."

Result

根据需要。 <Result () {'a': 'foo', 'c': 'hello', 'b': 'bar1, bar2'}> 也等同于原始reconstructed_obj