文章来自《Python cookbook》.

翻译仅仅是为了个人学习,其它商业版权纠纷与此无关!

-- 61.182.251.99 [2004-10-09 23:27:13]

描述

Overriding a Built-In Method

覆盖内建方法

Credit: Dave Haynes

问题 Problem

You need to wrap (or, in Python 2.2, inherit from) a list or tuple, delegating several operations to it, and want to provide proper slicing (i.e., through the special method _ _getitem_ _).

需要包装(或者在python 2.2 中使用继承)list或tuple,代理它们的一些操作, 同时提供正确的slice切片分块行为(使用方法 getitem).

解决 Solution

In most cases, overriding special methods of built-in objects when you inherit from those objects (or wrap them with automatic delegation, which is not technically an override) poses no special challenge. When inheriting in Python 2.2, you can call the special method of the superclass with the usual unbound-method syntax. When wrapping, use the syntax that is specific to the operation, such as self.data[someindex] for indexing.

多数情形下, 运用继承或者封装代理操作(技术上这样不是覆盖)来覆盖内建对象的特定方法没有什么挑战。 在python 2.2中运用继承方法,可以调用基类的具有各种方法签名的特定方法。运用封装的方法,应该使用对应于操作的具有特定方法签名的函数,例如,索引操作应具有如下形式: self.data[someindex]。

Slicing is harder, because while slicing should go through the same special method _ _getitem_ _ as indexing (since Python 2.0), lists and tuples still implement an older approach: the more limited special method _ _getslice_ _ (and similarly for _ _setitem_ _ versus _ _setslice_ _ and _ _delitem_ _ versus _ _delslice_ _). So, you must provide a remedy, normally with a try/except:

实现切片分块困难些, 正常情况下切片分块应该使用与索引操作相同的方法getitem(python 2.0以后)。不过,由于 list和tuple仍然保留了slice的旧方法:受限制的特别的getslice方法(相对于setitemsetslice,相对于delitemdelslice ), 因此, 必须提供一个措施来解析方法调用,一般使用 try/except:

   1 class SliceTester:
   2     def _ _init_ _(self):
   3         self.data = ['zero', 'one', 'two', 'three', 'four']
   4 
   5     def  _ _getitem_ _(self, indexOrSlice):
   6         try:
   7             return self.data[indexOrSlice]
   8         except TypeError:
   9             return self.data[indexOrSlice.start:indexOrSlice.stop]

讨论 Discussion

When a user-defined class wraps (or, in Python 2.2, inherits from) a list or tuple, it often needs to define the _ _set*_ _ and _ _get*_ _ special methods and delegate part or all of their operation to the wrapped (or inherited) built-in object to provide the correct access to the data.

当封装(python2.2中,用继承)list或者tuple,生成自定义类型时, 一般需要定义特殊的set*get*方法,代理一些(全部)此类操作到被封装的内置对象, 由被封装对象提供对底层数据的正确访问。

The documentation for Python 2.0 and later deprecates the use of _ _getslice_ _ and _ _setslice_ _. Instead, it suggests providing suitably extended versions of _ _getitem_ _ and _ _setitem_ _. This is a truly excellent idea because it enables the use of the extended-form slicing approaches (including step, ellipsis, and so on) that Numeric Python has made so deservedly popular among its regular users. Unfortunately, if you try to pass a slice object to the item-oriented special methods of a list or tuple object, you get a TypeError; the underlying C API still insists on receiving integer parameters, not slice objects in all their glory, whatever the documentation may say.

python 2.0和更高版本文档中,废除了函数getslice he setslice。建议提供getitemsetitem的功能扩展版本来代替。这个想法真不错,这样可以获得类似模块Numeric Python提供的功能。现在可以自己实现虔诚的Numeric Python用户使用的功能:扩展形式(包括步长、缺省值等)的分块操作。不幸的是,对于list或者tuple的某些特定函数,传入的slice对象作为参数,还是会抛出异常TypeError: 底层的C API还是仅仅接受整数参数,无论Numeric Python的文档如何描述,到了这里都行不通。

Fortunately, working around this problem isn't as dramatic as all that. You just need to trap the TypeError you get from trying to index an old-fashioned sequence with a slice, and remedy it suitably. Here's the typical self-test code that you can append to the recipe's module and execute when it is run as a main script:

幸运的是,实现这个功能不是太复杂。实现仅需要使用旧版本形式的分块操作,获取TypeError异常,进行合适异常处理。

下面是自测试代码:

   1 if _ _name_ _ == "_ _main_ _":
   2     theSlice = SliceTester(  )
   3     a = theSlice[2]
   4     b = theSlice[:3]
   5     print a
   6     print b

In the recipe's SliceTester example class, the remedy is pretty minimal; it's just an attempt to use start and stop attributes of the noninteger index (presumably an instance of the slice built-in type). You may want to do a lot more (implement step, ellipsis, and so on).

食谱中类SliceTester的异常处理很简单;仅仅使用了非整数的索引(假设是内置的slice类型)的start和stop属性。可以做更多处理(包括步长、缺省值等)。

Note that this recipe doesn't cover all of the cases in which slices can be used. There is a third argument to the slice operator that defines the step, or stride, of the slicing. For example, if data is a Numeric Python array (the only widely used software that supports slicing in all its glory), data[0:101:10] returns the sequence data[0], data[10], data[20]梪p to data[100]. Similarly, data[[|-1]] returns a sequence containing the contents of data reversed. The third argument to the slice operator is stored in the step attribute of slice objects and is set to None if a step isn't specified (as in list[start:end]). Given this, it shouldn't be a surprise that the recipe shown earlier will not magically add support for steps to objects that don't support new-style slices.

食谱并未覆盖分块操作可能应用的所有场景。 分块索引运算可以有第三个参数:定义步长(步幅)。比如,一个Numeric Python的数组(很强的说! 仅有的所有部分都支持分块操作并得到广泛应用的模块) data[0:101:10]给出data[0],data[10],data[20]直道data[100]. 类似的,data[ : :-1 ]逆序返回所有元素的list. 第三个参数可以从slice对象的step属性获得,如果未指定,step设置为None. 这样,对于不支持新形式分块操作的对象, 上面食谱中代码也不会变魔术般的支持分块操作的步长参数.

The point of this recipe is that you must be aware of these limitations and take precautionary measures. Also, don't type-test for an index of type slice. If normal indexing refuses the index, you are better off catching the TypeError in an except clause and entering another try/except in which you try to use the index as the slice you now expect it to be. This lets client code pass you objects that are polymorphic to slice objects.

本食谱的的要点是: 必须清楚这些限制条件,采取预防措施. 同时,不要对索引参数进行是否为slice对象的类型测试。 如果索引参数被正常的索引操作拒绝,那么最好在except语句中捕获TypeError异常并进行处理: 进入新的try/except部分,尝试将索引参数作为期望的slice对象使用. 这样对于客户输入的slice对象可以做到多态处理.

参考 See Also

The section of the Language Reference on slicing; the description of the slice built-in function in the Library Reference.

Python语言参考 Slicing部分

python库参考 内置slice函数

PyCkBk-5-2 (last edited 2009-12-25 07:15:05 by localhost)