我正在python中编写YAML配置序列化(使用YAML,因为它是一个对象树配置,我希望配置尽可能具有人工可读性。)
我有几个问题:
在Java中你有.is() here s.a. @JsonInclude(Include.NON_NULL)
以及为json文件执行此操作的其他人,我在python中发现yaml(甚至对于JSON)没有类似的东西,我知道如何编写它(使用YAML包API)但我不想如果它已经是在某个地方实施。
我要序列化的类的示例
class Locator(object):
def __init__(self, multiple=False):
# configurable parameters
self.multiple = multiple
# internal parameters (used in locate method)
self.precedents = []
self.segments
self.probabilities = []
def locate(self, message):
"""
do stuff to locate stuff on message
""" . . .
yield segment
这里我们看到保存配置参数(多个)的根类,如果它是True,我只希望序列化,以及在其操作s.a中使用的其他成员。儿子(先例)等...我根本不想序列化
任何人都可以帮我吗?
答案 0 :(得分:2)
我认为诚实的答案可能不是",原因是你在这里所达到的并不是真正的Python惯用语。如果你稍微眯一下,那么Python dicts和JSON对象之间有很强的相似性 - 而且斜视更多,YAML看起来像JSON的空白方言 - 所以当我们需要从Python序列化thing
时我们倾向于将一些thing
的自定义映射写入dict,将其填充到JSON / YAML序列化器中,并完成它。
在thing => dict
步骤中,有一些快捷方式和惯用法可以派上用场。例如,当您在其上调用asdict
时,带有方法的namedTuple子类将使所述方法退出:
In [1]: from collections import namedtuple
In [2]: class Locator(namedtuple("Locator", "foo bar baz")):
...: def hide(self):
...: pass
...:
In [3]: wow = Locator(1,2,3)
In [4]: wow._asdict()
Out[4]: OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)])
当然元组是不可变的,所以如果你真的需要一个具有可变属性的类,这不是一个通用的解决方案,而且这并没有解决你在声明中从序列化中删除某些属性的愿望。方式。
可能符合您需求的一个不错的第三方库是attrs ...这个库提供了类似于一个具有很多可定制性的特殊命名元组,包括过滤器和默认值,您可以使用它们在你觉得舒服的东西。它不是1:1,而是你在这里所达到的目标,但它可能是一个开始。
答案 1 :(得分:0)
这是我同时为JSON写的一个解决方案,但是它非常具体,我很想找到一个已经解决这个问题的软件包更加通用
class LocatorEncoder(json.JSONEncoder):
"""
custom Locator json encoder to encode only configuration related parameters
it encodes only :
1. protected data members (whose names start with a single '_'
2. members that are different from their default value
NOTE: for this filtering encoder to work two strong conventions must be upheld, namely:
1. Configuration data members must start with a single preceding '_'
2. They must differ from their correlated __init__ parameter by only that '_'
"""
@staticmethod
def get_default_args(f):
return {
k: v.default
for k, v in inspect.signature(f).parameters.items()
if v.default is not inspect.Parameter.empty
}
@staticmethod
def filter(d, defaults):
"""
this is the filtering method
:param d: dictionary of members to filter
:param defaults: default values to filter out
:return: ordered dictionary with only protected members ('_') that do not have their default value
the return dictionary is ordered because it prints nicer
"""
filtered = OrderedDict()
for k, v in d.items():
# this is the filter logic (key starts with one _ and is not its default value)
if (re.match(r'_[^_]', k)) and (k[1:] not in defaults or defaults[k[1:]] != v):
filtered[k] = v
return filtered
def default(self, o):
"""
iterate on classes in the objects mro and build a list of default constructor values
:param o: the object to be json encoded
:return: encoded dictionary for the object serialization
"""
if isinstance(o, Locator):
defaults = {}
for cl in o.__class__.mro():
# iterate on all the default arguments of the __init__ method
for k, v in self.get_default_args(cl.__init__).items():
# update the key with value if it doesn't already exist
defaults[k] = defaults.get(k, v)
# build the filtered configuration data members and add precedent in a recursive call to this default method
filtered_dictionary = self.filter(o.__dict__, defaults)
precedents = []
for precedent in o.precedents:
precedents.append(self.default(precedent))
filtered_dictionary["precedents"] = precedents
return {'__{}__'.format(o.__class__.__name__): filtered_dictionary}
return super().default(self, o)