This issue tracker has been migrated to GitHub ,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2011年12月02日 16:29 by sbt, last changed 2022年04月11日 14:57 by admin. This issue is now closed.
| Files | ||||
|---|---|---|---|---|
| File name | Uploaded | Description | Edit | |
| pickle_qualname.patch | sbt, 2011年12月02日 16:29 | |||
| Messages (5) | |||
|---|---|---|---|
| msg148759 - (view) | Author: Richard Oudkerk (sbt) * (Python committer) | Date: 2011年12月02日 16:29 | |
The attached patch makes pickle use an object's __qualname__ attribute if __name__ does not work. This makes nested classes, unbound instance methods and static methods picklable (assuming that __module__ and __qualname__ give the correct "address"). BTW, It would not be hard to make instance methods picklable (and, as a by-product, class methods) by giving instance methods a __reduce__ method equivalent to def __reduce__(self): return (getattr, (self.__self__, self.__name__)) Is there any reason not to make such a change? |
|||
| msg148770 - (view) | Author: Antoine Pitrou (pitrou) * (Python committer) | Date: 2011年12月02日 19:10 | |
> The attached patch makes pickle use an object's __qualname__ attribute > if __name__ does not work. > > This makes nested classes, unbound instance methods and static methods > picklable (assuming that __module__ and __qualname__ give the correct > "address"). Thanks. I'm not sure that's a good approach for protocol 3: some pickles created by Python 3.3 would not be readable by Python 3.2. That's why I initially added that idea to PEP 3154, for a hypothetical protocol 4. However, perhaps it is possible to use the same reduce/getattr trick you are proposing for instance methods? > BTW, It would not be hard to make instance methods picklable (and, as > a by-product, class methods) by giving instance methods a __reduce__ > method equivalent to > > def __reduce__(self): > return (getattr, (self.__self__, self.__name__)) > > Is there any reason not to make such a change? I don't see any personally. Actually, multiprocessing has a Pickler subclass called ForkingPickler which does something similar (in Lib/multiprocessing/forking.py). |
|||
| msg148903 - (view) | Author: Alexandre Vassalotti (alexandre.vassalotti) * (Python committer) | Date: 2011年12月06日 02:36 | |
This might not be the case anymore, but __module__ can sometime be None. There is some discussion about this in Issue 3657. We should define the expected behavior when this happens. Also, I don't think backward-compatibility of the protocol is a big issue if we use the getattr approach. I feel bumping the protocol version is only necessary if we introduce new opcodes or change their interpretation. |
|||
| msg148920 - (view) | Author: Richard Oudkerk (sbt) * (Python committer) | Date: 2011年12月06日 14:52 | |
It looks like Issue 3657 is really about builtin methods (i.e. builtin_function_or_method objects where __self__ is not a module). It causes no problem for normal python instance methods. If we tried the getattr approach for builtin methods too then it should only be used as a fallback when saving as a global will not work. |
|||
| msg168820 - (view) | Author: Richard Oudkerk (sbt) * (Python committer) | Date: 2012年08月21日 23:16 | |
There is a cute way to use operator.attrgetter to produce backwards compatible pickles using the qualname:
import pickle, copyreg, operator, sys, pickletools, types
class AttrGetter(object):
def __init__(self, name):
self.name = name
def __call__(self): # pretend to be callable
raise RuntimeError
def __reduce__(self):
return operator.attrgetter, (self.name,)
def reduce_by_qualname(obj):
mod = sys.modules[obj.__module__]
first, rest = obj.__qualname__.split('.', 1)
firstobj = getattr(mod, first)
assert operator.attrgetter(rest)(firstobj) is obj
return AttrGetter(rest), (firstobj,)
# FunctionType defaults to save_global but uses fallback if it fails
copyreg.pickle(types.FunctionType, reduce_by_qualname)
class A(object):
class B(object):
class C(object):
@staticmethod
def foo():
print("foo foo foo")
def bar():
print("bar bar bar")
for obj in [A.B.C.foo, bar]:
data = pickle.dumps(obj, 2)
pickletools.dis(data)
func = pickle.loads(data)
assert func is obj
func()
This produces
0: \x80 PROTO 2
2: c GLOBAL 'operator attrgetter'
23: q BINPUT 0
25: X BINUNICODE 'B.C.foo'
37: q BINPUT 1
39: \x85 TUPLE1
40: q BINPUT 2
42: R REDUCE
43: q BINPUT 3
45: c GLOBAL '__main__ A'
57: q BINPUT 4
59: \x85 TUPLE1
60: q BINPUT 5
62: R REDUCE
63: q BINPUT 6
65: . STOP
highest protocol among opcodes = 2
foo foo foo
0: \x80 PROTO 2
2: c GLOBAL '__main__ bar'
16: q BINPUT 0
18: . STOP
highest protocol among opcodes = 2
bar bar bar
|
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022年04月11日 14:57:24 | admin | set | github: 57729 |
| 2013年11月30日 04:52:45 | alexandre.vassalotti | set | status: open -> closed versions: + Python 3.4, - Python 3.3 resolution: duplicate assignee: alexandre.vassalotti superseder: Implement PEP 3154 (pickle protocol 4) stage: patch review -> resolved |
| 2012年08月21日 23:16:03 | sbt | set | messages: + msg168820 |
| 2012年06月24日 07:31:31 | hynek | set | nosy:
- hynek |
| 2012年02月26日 18:43:44 | lukasz.langa | set | nosy:
+ lukasz.langa |
| 2012年01月22日 19:48:15 | hynek | set | nosy:
+ hynek |
| 2011年12月06日 14:52:15 | sbt | set | messages: + msg148920 |
| 2011年12月06日 02:36:52 | alexandre.vassalotti | set | messages: + msg148903 |
| 2011年12月02日 19:13:05 | pitrou | set | nosy:
+ alexandre.vassalotti |
| 2011年12月02日 19:10:40 | pitrou | set | messages: + msg148770 |
| 2011年12月02日 16:36:13 | vstinner | set | nosy:
+ vstinner |
| 2011年12月02日 16:33:31 | ezio.melotti | set | type: enhancement components: + Extension Modules stage: patch review |
| 2011年12月02日 16:29:12 | sbt | create | |