拦截Python中的切片操作

10 投票
4 回答
2687 浏览
提问于 2025-04-15 13:05

我想要模仿一个普通的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]

这个方法在其他操作,比如appendextend上是有效的,只是在切片操作上不行。

补充说明:真正的问题是我在用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)

撰写回答