2016-10-11 8 views
6

適切なときにクラスをスライスとして表現するにはどうすればよいですか?スライスのように振舞うオブジェクトを作成する

これは動作しませんでした:

class MyThing(object): 
    def __init__(self, start, stop, otherstuff): 
     self.start = start 
     self.stop = stop 
     self.otherstuff = otherstuff 
    def __index__(self): 
     return slice(self.start, self.stop) 

予想される出力:

>>> thing = MyThing(1, 3, 'potato') 
>>> 'hello world'[thing] 
'el' 

実際の出力:

sliceから継承
TypeError: __index__ returned non-(int,long) (type slice) 

はどちらか動作しません。

+0

を必要とします'__index __()'は 'slice'の代わりに' int'を返すことを強調しており、単一の整数を返すとうまく動作するようです。 –

+0

なぜ、これをやりたいのですか? – MisterMiyagi

+2

私はスライスと同じように動作する豊かなスライスを作っていますが、いくつかの拡張機能も備えています。 – wim

答えて

5

TLDR:listtupleなどの組み込み型の場合は、カスタムクラスをsliceに置き換えることはできません。


__index__方法は、定義により、Pythonで整数(the Data Modelを参照)インデックスを提供するために純粋に存在します。オブジェクトを解決するためにオブジェクトをsliceに使用することはできません。

sliceは、Pythonによって特別に処理されるようです。インターフェイスには実際のスライスが必要です。 (indicesメソッドを含む)その署名を提供するだけでは十分ではありません。あなたが知っているように、あなたはそれを継承することはできないので、新しいタイプのsliceを作成することはできません。 Cythonでもそれを継承することはできません。


なぜsliceが特別なのですか?あなたが喜んで尋ねました。 CPythonの内部へようこそ。これを読んでから手を洗ってください。

スライスオブジェクトはslice.rstで記述されています。これらの2人の男に注意してください。

... C:VAR ::このPyTypeObject PySlice_Type

スライスオブジェクトの型のオブジェクトを。これは、 Python層のクラス:sliceと同じです。

.. c:function :: int PySlice_Check(PyObject * ob) obがスライスオブジェクトの場合はtrueを返します。 obは、NULLであってはなりません。

#define PySlice_Check(op) (Py_TYPE(op) == &PySlice_Type) 

のでのみsliceタイプは、ここで許可されている:として今、これは実際にsliceobject.hで実装されて

。このチェックは、のインデックスプロトコルを使用しようとしたときにlist_subscript(およびtuple subscript、...)で実際に使用されます(したがって、スライスに__index__があることは悪い考えです)。カスタムコンテナクラスは自由に__getitem__を上書きし、独自のルールを使用しますが、それはlist(およびtuple、...)のようになります。

今、なぜsliceをサブクラス化できないのですか?さて、typeには、何かをサブクラス化できるかどうかを示すフラグが実際にあります。 hereチェックし、あなたが見ているエラーが生成されます。

if (!PyType_HasFeature(base_i, Py_TPFLAGS_BASETYPE)) { 
     PyErr_Format(PyExc_TypeError, 
        "type '%.100s' is not an acceptable base type", 
        base_i->tp_name); 
     return NULL; 
    } 

私はslice(UN)がこの値を設定しますが、1は、このエラーを取得しているという事実は、それがない意味どのように追跡することができていません。つまり、サブクラス化することはできません。閉会の辞


:いくつかの長い忘れC-(非)-skillsを思い出した後、私は、これは、厳密な意味での最適化に関するものではありませんかなり確信しています。すべての既存の小切手やトリックはまだ動作します(少なくとも私が見つけたもの)。

私の手を洗ってインターネットで掘り起こした後、私は同様の「問題」への参照をいくつか見つけました。誰かがそれをサブクラス化するために作業 ボランティアない限り、Cで実装さTim Peters has said all there is to say:

何もサブクラス化ではありません。誰も、[ここに名前を入れてください] をサブクラス化できるようにする作業を志願していませんでした。それは確か私のリストの最上部にはなかったwink

this threadも参照してください。サブクラス以外のタイプの短い説明については、this threadを参照してください。 JythonPystonIronPythonとPyPyは(彼らはそれを行う方法を見つけることができませんでしたが、彼らはありません):

は、実質的にすべての代替通訳は様々な程度に動作を再現します。

+1

実際のスライスを必要とするインターフェースのリファレンスを提供できますか?サブクラスを使ってそれをやっているのはどういうわけか不可能ですか? "それはあなたがそれをすることはできないようです"とは特に説得力のある答えではありません。 – wim

+0

私はあなたがそれをすることができないようだと言っているわけではありません。私はスライスを提供する必要があり、新しいタイプのスライスを作成することはできないと言っています。サブクラスを作成できない場合は、サブクラスでトリックすることは不可能です。つまり、私は何かが決定的になるかどうかを見るために情報源を見ています。 – MisterMiyagi

+0

これはダックタイピングに違反しているようです。 '__getitem__'がスライスオブジェクトを受け入れるならば、それはおそらくスライスのように動作するものを受け入れるべきです。 – wim

-2

スライスをreturnタイプにすることはできません。メソッドがこれをサポートしていないからです。 __index__special method hereについて詳しく読むことができます。 私は直接あなたのクラスで関数を呼び出し、回避策を考え出すことができます:

class MyThing(object): 
     def __init__(self, start, stop, otherstuff): 
      self.start = start 
      self.stop = stop 
      self.otherstuff = otherstuff 

     def __index__(self): 
      return slice(self.start, self.stop) 

    thing = MyThing(1, 3, 'potato') 
    print 'Hello World'[thing.__index__()] 

これはelを返します。

+1

いいえ、それは左側の '__getitem__'によって暗黙的に呼び出されました。あなたの提案は 'def as_slice(self):return slice(self.start、self.stop)'のような 'as_slice'メソッドを定義することと同じです。 – wim

+0

スライスにオブジェクトを使用できるようにPEP 357を読んだら、 '__index__' *の戻り値の型が' int'か 'long'かを知っているでしょう。スライスはそこでは機能しません。 – Mangohero1

+1

'__index__'を呼び出すポイントは何ですか?彼はその後スライスを渡すことができます - いずれかの方法でカスタムクラスではありません。 – MisterMiyagi

3

私はForbiddenfruitを使用DARK MAGIC

ごめんなさいとPythonのPythonの組み込みnew方法私はこれを行うことができました:

from forbiddenfruit import curse 


class MyThing(int): 
    def __new__(cls, *args, **kwargs): 
     magic_slice = slice(args[0], args[1]) 
     curse(slice, 'otherstuff', args[2]) 

     return magic_slice 

thing = MyThing(1, 3, 'thing') 
print 'hello world'[thing] 
print thing.otherstuff 

出力:

>>> el 
>>> thing 

は、私は誰もが、それはあなたがあなたの構造にもう一度考えなければならない、私はITは非常に多くの副作用があり、生産コードでそれを使うことはない、不可能であるという理由だけで挑戦としてそれを書いたから、うまく

+0

ああ、猿の鳴き声。うーん。残念なことに、このハックは私が( "__or__"、 "__and__"などで)悩んでいたかったメソッドでは機能しませんが、リマインダーのためには+1を持っています。 – wim

+0

これは、新しいクラスを作成していないスライスを変更しています。後で 'slice(2,3,4).otherstuff'を実行することができ、' type(MyThing(1,2,3)) 'は実際には' slice'です。 'MyThing'の実際のインスタンスはまだ' slice'sとして動作しません。 – MisterMiyagi

関連する問題