我一直在努力获得一些动态创建的类型(即通过调用3-arg type()
创建的类型)来进行pickle和unpickle。我一直在使用this module switching trick隐藏模块用户的详细信息,并提供干净的语义。
我已经学到了很多东西:
getattr
上找到该类型getattr
找到的内容一致,也就是说,如果我们调用pickle.dumps(o)
,则必须为type(o) == getattr(module, 'name of type')
我被困住的地方似乎仍有一些奇怪的事情发生 - 它似乎是在意外的事情上调用__getstate__
。
这是我用最简单的设置重现问题,使用Python 3.5进行测试,但如果可能的话我想回到3.3:
# module.py
import sys
import functools
def dump(self):
return b'Some data' # Dummy for testing
def undump(self, data):
print('Undump: %r' % data) # Do nothing for testing
# Cheaty demo way to make this consistent
@functools.lru_cache(maxsize=None)
def make_type(name):
return type(name, (), {
'__getstate__': dump,
'__setstate__': undump,
})
class Magic(object):
def __init__(self, path):
self.path = path
def __getattr__(self, name):
print('Getting thing: %s (from: %s)' % (name, self.path))
# for simple testing all calls to make_type must end in last x.y.z.last
if name != 'last':
if self.path:
return Magic(self.path + '.' + name)
else:
return Magic(name)
return make_type(self.path + '.' + name)
# Make the switch
sys.modules[__name__] = Magic('')
然后快速练习:
import module
import pickle
f=module.foo.bar.woof.last()
print(f.__getstate__()) # See, *this* works
print('Pickle starts here')
print(pickle.dumps(f))
然后给出:
Getting thing: foo (from: )
Getting thing: bar (from: foo)
Getting thing: woof (from: foo.bar)
Getting thing: last (from: foo.bar.woof)
b'Some data'
Pickle starts here
Getting thing: __spec__ (from: )
Getting thing: _initializing (from: __spec__)
Getting thing: foo (from: )
Getting thing: bar (from: foo)
Getting thing: woof (from: foo.bar)
Getting thing: last (from: foo.bar.woof)
Getting thing: __getstate__ (from: foo.bar.woof)
Traceback (most recent call last):
File "test.py", line 7, in <module>
print(pickle.dumps(f))
TypeError: 'Magic' object is not callable
我不希望在__getstate__
上看到任何查找module.foo.bar.woof
的内容,但即使我们通过添加强制查找失败:
if name == '__getstate__': raise AttributeError()
进入我们的__getattr__
它仍然失败:
Traceback (most recent call last):
File "test.py", line 7, in <module>
print(pickle.dumps(f))
_pickle.PicklingError: Can't pickle <class 'module.Magic'>: it's not the same object as module.Magic
是什么给出的?我错过了__spec__
的内容吗? docs for __spec__
几乎只是强调设置它,但似乎并没有真正解释太多。
更重要的是,更大的问题是我应该如何通过伪模块的__getattr__
实现pickle以编程方式生成类型?
(很显然,一旦我设法让pickle.dumps
生成我希望pickle.loads
用同样的东西调用undump
的内容,我可以使用public static async Task<string> GetAccessTokenAsync(string clientId, string appKey, string resourceId)
{
string aadInstance = "https://login.microsoftonline.com/{0}";
string tenant = "<TENANT>.onmicrosoft.com";
string authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
AuthenticationContext authContext = new AuthenticationContext(authority);
ClientCredential clientCredential = new ClientCredential(clientId, appKey);
AuthenticationResult authenticationResult = null;
authenticationResult = await authContext.AcquireTokenAsync(resourceId, clientCredential);
return authenticationResult.AccessToken;
}
答案 0 :(得分:1)
要挑选f
,pickle
需要挑选f
的课程module.foo.bar.woof.last
。
文档并不声称支持任意课程。 They claim the following:
可以腌制以下类型:
- ...
- 在模块顶层定义的类
module.foo.bar.woof.last
未定义在模块的顶层,甚至是module
之类的假装模块。在这个未得到官方支持的案例中,pickle逻辑最终试图挑选module.foo.bar.woof
,here:
elif parent is not module:
self.save_reduce(getattr, (parent, lastname))
或here
else if (parent != module) {
PickleState *st = _Pickle_GetGlobalState();
PyObject *reduce_value = Py_BuildValue("(O(OO))",
st->getattr, parent, lastname);
status = save_reduce(self, reduce_value, NULL);
由于多种原因, module.foo.bar.woof
无法被腌制。它为所有不受支持的方法查找返回一个不可调用的Magic
实例,例如__getstate__
,这是您的第一个错误来源。模块切换事情阻止找到Magic
类来挑选它,这是你的第二个错误来自的地方。可能存在更多不兼容性。
答案 1 :(得分:0)
看起来,并且已经证明,使callable
课程只是一个漂移的另一个错误的方向,谢天谢地this hack,我可以找到一个解决方法,使该类可以通过其TYPE重复。在错误<class 'module.Magic'>: it's not the same object as module.Magic
的上下文之后,pickler不会迭代通过相同的调用来呈现与另一个类型不同的类型,这是一个主要的常见问题,即对于这个实例,pickle self instanciating classes它的类,解决方案是使用类型@mock.patch('module.Magic', type(module.Magic))
修补类这是一个简短的答案。
<强> Main.py 强>
import module
import pickle
import mock
f=module1.foo.bar.woof.last
print(f().__getstate__()) # See, *this* works
print('Pickle starts here')
@mock.patch('module1.Magic', type(module1.Magic))
def pickleit():
return pickle.dumps(f())
print(pickleit())
魔术课
class Magic(object):
def __init__(self, value):
self.path = value
__class__: lambda x:x
def __getstate__(self):
print ("Shoot me! i'm at " + self.path )
return dump(self)
def __setstate__(self,value):
print ('something will never occur')
return undump(self,value)
def __spec__(self):
print ("Wrong side of the planet ")
def _initializing(self):
print ("Even farther lost ")
def __getattr__(self, name):
print('Getting thing: %s (from: %s)' % (name, self.path))
# for simple testing all calls to make_type must end in last x.y.z.last
if name != 'last':
if self.path:
return Magic(self.path + '.' + name)
else:
return Magic(name)
print('terminal stage' )
return make_type(self.path + '.' + name)
即使假设这不是击球的边缘,我也可以看到内容被丢弃到我的控制台中。