拦截Python中的切片操作
我想要模仿一个普通的Python列表,不过每当通过切片添加或删除元素时,我想要“保存”这个列表。这样做可能吗?这是我尝试的代码,但它永远不会打印出“saving”。
class InterceptedList(list):
def addSave(func):
def newfunc(self, *args):
func(self, *args)
print 'saving'
return newfunc
__setslice__ = addSave(list.__setslice__)
__delslice__ = addSave(list.__delslice__)
>>> l = InterceptedList()
>>> l.extend([1,2,3,4])
>>> l
[1, 2, 3, 4]
>>> l[3:] = [5] # note: 'saving' is not printed
>>> l
[1, 2, 3, 5]
这个方法在其他操作,比如append
和extend
上是有效的,只是在切片操作上不行。
补充说明:真正的问题是我在用Jython而不是Python,我忘记了这一点。问题下的评论是对的。这段代码在Python(2.6)中运行得很好。不过,这段代码和答案在Jython中都不管用。
4 个回答
5
好了,别再猜测了。我们来用事实说话吧!
从我所了解的情况来看,关键是你必须重写这两组方法。
如果你想实现撤销/重做功能,可能应该尝试使用撤销栈和一组可以自己执行(do())和撤销(undo())的操作。
代码
import profile
import sys
print sys.version
class InterceptedList(list):
def addSave(func):
def newfunc(self, *args):
func(self, *args)
print 'saving'
return newfunc
__setslice__ = addSave(list.__setslice__)
__delslice__ = addSave(list.__delslice__)
class InterceptedList2(list):
def __setitem__(self, key, value):
print 'saving'
list.__setitem__(self, key, value)
def __delitem__(self, key):
print 'saving'
list.__delitem__(self, key)
print("------------Testing setslice------------------")
l = InterceptedList()
l.extend([1,2,3,4])
profile.run("l[3:] = [5]")
profile.run("l[2:6] = [12, 4]")
profile.run("l[-1:] = [42]")
profile.run("l[::2] = [6,6]")
print("-----------Testing setitem--------------------")
l2 = InterceptedList2()
l2.extend([1,2,3,4])
profile.run("l2[3:] = [5]")
profile.run("l2[2:6] = [12,4]")
profile.run("l2[-1:] = [42]")
profile.run("l2[::2] = [6,6]")
Jython 2.5
C:\Users\wuu-local.pyza\Desktop>c:\jython2.5.0\jython.bat intercept.py
2.5.0 (Release_2_5_0:6476, Jun 16 2009, 13:33:26)
[Java HotSpot(TM) Client VM (Sun Microsystems Inc.)]
------------Testing setslice------------------
saving
3 function calls in 0.035 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:0(<module>)
1 0.000 0.000 0.000 0.000 intercept.py:9(newfunc)
1 0.034 0.034 0.035 0.035 profile:0(l[3:] = [5])
0 0.000 0.000 profile:0(profiler)
saving
3 function calls in 0.005 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.001 0.001 <string>:0(<module>)
1 0.001 0.001 0.001 0.001 intercept.py:9(newfunc)
1 0.004 0.004 0.005 0.005 profile:0(l[2:6] = [12, 4])
0 0.000 0.000 profile:0(profiler)
saving
3 function calls in 0.012 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:0(<module>)
1 0.000 0.000 0.000 0.000 intercept.py:9(newfunc)
1 0.012 0.012 0.012 0.012 profile:0(l[-1:] = [42])
0 0.000 0.000 profile:0(profiler)
2 function calls in 0.004 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:0(<module>)
1 0.004 0.004 0.004 0.004 profile:0(l[::2] = [6,6])
0 0.000 0.000 profile:0(profiler)
-----------Testing setitem--------------------
2 function calls in 0.004 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:0(<module>)
1 0.004 0.004 0.004 0.004 profile:0(l2[3:] = [5])
0 0.000 0.000 profile:0(profiler)
2 function calls in 0.006 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:0(<module>)
1 0.006 0.006 0.006 0.006 profile:0(l2[2:6] = [12,4])
0 0.000 0.000 profile:0(profiler)
2 function calls in 0.004 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 <string>:0(<module>)
1 0.004 0.004 0.004 0.004 profile:0(l2[-1:] = [42])
0 0.000 0.000 profile:0(profiler)
saving
3 function calls in 0.007 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.002 0.002 <string>:0(<module>)
1 0.001 0.001 0.001 0.001 intercept.py:20(__setitem__)
1 0.005 0.005 0.007 0.007 profile:0(l2[::2] = [6,6])
0 0.000 0.000 profile:0(profiler)
Python 2.6.2
C:\Users\wuu-local.pyza\Desktop>python intercept.py
2.6 (r26:66721, Oct 2 2008, 11:35:03) [MSC v.1500 32 bit (Intel)]
------------Testing setslice------------------
saving
4 function calls in 0.002 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.002 0.002 0.002 0.002 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 intercept.py:9(newfunc)
1 0.000 0.000 0.002 0.002 profile:0(l[3:] = [5])
0 0.000 0.000 profile:0(profiler)
saving
4 function calls in 0.000 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 intercept.py:9(newfunc)
1 0.000 0.000 0.000 0.000 profile:0(l[2:6] = [12, 4])
0 0.000 0.000 profile:0(profiler)
saving
4 function calls in 0.000 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 intercept.py:9(newfunc)
1 0.000 0.000 0.000 0.000 profile:0(l[-1:] = [42])
0 0.000 0.000 profile:0(profiler)
3 function calls in 0.000 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 profile:0(l[::2] = [6,6])
0 0.000 0.000 profile:0(profiler)
-----------Testing setitem--------------------
3 function calls in 0.000 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 profile:0(l2[3:] = [5])
0 0.000 0.000 profile:0(profiler)
3 function calls in 0.000 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 profile:0(l2[2:6] = [12,4])
0 0.000 0.000 profile:0(profiler)
3 function calls in 0.000 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.000 0.000 <string>:1(<module>)
1 0.000 0.000 0.000 0.000 profile:0(l2[-1:] = [42])
0 0.000 0.000 profile:0(profiler)
saving
4 function calls in 0.003 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.000 0.000 :0(setprofile)
1 0.000 0.000 0.003 0.003 <string>:1(<module>)
1 0.002 0.002 0.002 0.002 intercept.py:20(__setitem__)
1 0.000 0.000 0.003 0.003 profile:0(l2[::2] = [6,6])
0 0.000 0.000 profile:0(profiler)
5
来自Python 3 文档:
__getslice__(), __setslice__() and __delslice__() were killed.
The syntax a[i:j] now translates to a.__getitem__(slice(i, j))
(or __setitem__() or __delitem__(), when used as an assignment
or deletion target, respectively).
5
"setslice" 和 "delslice" 这些东西已经不再推荐使用了。如果你想要进行拦截操作,你需要使用传递给 "setitem" 和 "delitem" 的 Python 切片对象。如果你想同时拦截切片和普通的访问,这段代码在 Python 2.6.2 中运行得很好。
class InterceptedList(list):
def addSave(func):
def newfunc(self, *args):
func(self, *args)
print 'saving'
return newfunc
def __setitem__(self, key, value):
print 'saving'
list.__setitem__(self, key, value)
def __delitem__(self, key):
print 'saving'
list.__delitem__(self, key)