问题描述
我有一个类,它的实例需要按照用户的指示格式化输出.有一个默认格式,可以覆盖.我是这样实现的:
I have a class whose instances need to format output as instructed by the user. There's a default format, which can be overridden. I implemented it like this:
class A:
def __init__(self, params):
# ...
# by default printing all float values as percentages with 2 decimals
self.format_functions = {float: lambda x : '{:.2%}'.format(x)}
def __str__(self):
# uses self.format_functions to format output
# ...
a = A(params)
print(a) # uses default output formatting
# overriding default output formatting
# float printed as percentages 3 decimal digits; bool printed as Y / N
a.format_functions = {float : lambda x: '{:.3%}'.format(x),
bool : lambda x: 'Y' if x else 'N'}
print(a)
可以吗?如果有更好的设计方法,请告诉我.
Is it ok? Let me know if there is a better way to design this.
不幸的是,我需要pickle这个类的实例.但是只有定义在模块顶层的函数才能被pickle;lambda
函数是不可选择的,所以我的 format_functions
实例属性打破了pickling.
Unfortunately, I need to pickle instances of this class. But only functions defined at the top level of the module can be pickled; lambda
functions are unpicklable, so my format_functions
instance attribute breaks the pickling.
我尝试重写它以使用类方法而不是 lambda 函数,但由于同样的原因仍然没有运气:
I tried rewriting this to use a class method instead of lambda functions, but still no luck for the same reason:
class A:
@classmethod
def default_float_format(cls, x):
return '{:.2%}'.format(x)
def __init__(self, params):
# ...
# by default printing all float values as percentages with 2 decimals
self.format_functions = {float: self.default_float_format}
def __str__(self):
# uses self.format_functions to format output
# ...
a = A(params)
pickle.dump(a) # Can't pickle <class 'method'>: attribute lookup builtins.method failed
请注意,即使我不覆盖默认值,这里的酸洗也不起作用;只是我分配了 self.format_functions = {float : self.default_float_format}
的事实打破了它.
Note that pickling here doesn't work even if I don't override the defaults; just the fact that I assigned self.format_functions = {float : self.default_float_format}
breaks it.
怎么办?我宁愿不污染命名空间并通过在模块级别定义 default_float_format
来破坏封装.
What to do? I'd rather not pollute the namespace and break encapsulation by defining default_float_format
at the module level.
顺便说一下,为什么 pickle
会创建这个限制?对于最终用户来说,这无疑是一种无端而巨大的痛苦.
Incidentally, why in the world does pickle
create this restriction? It certainly feels like a gratuitous and substantial pain to the end user.
推荐答案
对于类实例或函数(以及方法)的pickle,Python 的pickle 依赖于它们的名称可用作全局变量——字典中方法的引用指向一个在全局命名空间中不可用的名称 - 最好说模块命名空间" -
For pickling of class instances or functions (and therefore methods), Python's pickle depend that their name is available as global variables - the reference to the method in the dictionary points to a name that is not available in the global name space - which iis better said "module namespace" -
您可以通过自定义类的酸洗来规避这一点,通过创建__setstate__"和__getstate__"方法 - 但我认为你会更好,因为格式化功能不依赖于对象或对象的任何信息类本身(即使某些格式化函数这样做,您也可以将其作为参数传递),并在类范围之外定义一个函数.
You could circunvent that by customizing the pickling of your class, by creating teh "__setstate__" and "__getstate__" methods - but I think you be better, since the formatting function does not depend on any information of the object or of the class itself (and even if some formatting function does, you could pass that as parameters), and define a function outside of the class scope.
这确实有效(Python 3.2):
This does work (Python 3.2):
def default_float_format( x):
return '{:.2%}'.format(x)
class A:
def __init__(self, params):
# ...
# by default printing all float values as percentages with 2 decimals
self.format_functions = {float: default_float_format}
def __str__(self):
# uses self.format_functions to format output
pass
a = A(1)
pickle.dumps(a)
这篇关于酸洗类方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!