我正在尝试找到一种生成对象的一般方法,这些对象可以使用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__
类方法,并验证生成的对象parse
和obj
是否等效(此处定义为实例同一个班级,并有相同的词典;参见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}}。是否有一种优雅的方式来做到这一点?
答案 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
。