2017-09-08 8 views
4

私のクラスの出力をdir()に変更したいと思います。通常、他のすべてのオブジェクトでは、クラス内で独自の__dir__メソッドを定義することによって行われます。しかし、私のクラスでこれを行うと、呼び出されません。クラスの__dir__メソッドをオーバーライドする方法は?

dir()の出力をクラスに変更するにはどうすればよいですか?

+1

[*特殊メソッドの参照*](https://docs.python.org/3/reference/datamodel.html#special-lookup)を参照してください。 'type(X).__ dir__'は' X .__ dir__'ではなく、使われます。 –

+0

"通常、他のすべてのオブジェクトでは、クラス内で独自の' __dir__'メソッドを定義することによって行われます。 " - このオブジェクトと同じです。このクラスのクラス、つまりそのメタクラスで '__dir__'メソッドを定義します。 – user2357112

答えて

7

これは、dirが入力タイプの__dir__を呼び出すためです(type(inp).__dir__(inp)に相当)。クラスのインスタンスの場合はクラス__dir__を呼び出しますが、クラスで呼び出された場合はメタクラスの__dir__を呼び出します。

class X(object): 
    def __dir__(self): # missing self parameter 
     raise Exception("No!") 

dir(X()) # instance! 
# Exception: No! 

だから、あなたのクラスのdir(あなたのクラスのインスタンスではなく)をカスタマイズしたい場合は、あなたのXのためのメタクラスを追加する必要があります。@MSeifertは、すでに説明したように

import six 

class DirMeta(type): 
    def __dir__(cls): 
     raise Exception("No!") 

@six.add_metaclass(DirMeta) 
class X(object): 
    pass 

dir(X) 
# Exception: No! 
+0

それは働いて、ありがとう。しかし、現実には私は何かを "持ち上げたくない"ので、私はちょうど(unittestの検査から)いくつかの方法を取り除きたいので、私は '__dir__'の中で立ち往生しました。しかし、私は 'cls'コンテンツにアクセスする方法を見つけることができませんでした。私は知っていた、私はこの森に入るべきではない... –

+0

ああ、私は 'cls .__ dict __。keys()'を見つけた。正確に私が必要なもの。 –

+1

@GeorgeShuklin代わりに、 'type'の継承を使うこともできます。たとえば 'normal_dir = super().__ dir __()'(これは 'dir'を呼び出すときに通常表示されるものです)を返してから、' normal_dir'を修正する(追加/削除します)。しかし 'cls .__ dict __。keys()'も動作するはずです。 :) – MSeifert

2

は、dir呼び出します__dir__オブジェクトのクラスのattrbiute。したがって、が呼び出され、X.__dir__ではなく、呼び出されます。興味のある人だけに、正確に何が起こっているのかを見てみましょう。

dirの実装はbltinmodule.cである:

builtin_dir(PyObject *self, PyObject *args) 
{ 
    PyObject *arg = NULL; 

    if (!PyArg_UnpackTuple(args, "dir", 0, 1, &arg)) 
     return NULL; 
    return PyObject_Dir(arg); 
} 

dir機能は、API関数PyObject_Dirを呼び出します。 PyObject_Dir関数はobject.cに実装され:

PyObject * 
PyObject_Dir(PyObject *obj) 
{ 
    return (obj == NULL) ? _dir_locals() : _dir_object(obj); 
} 

PyObject_Dirは、2つのヘルパー関数を用いて定義されます。ここでのようにオブジェクトが渡されると、_dir_object関数が呼び出されます。また、object.cに実装されています。

static PyObject * 
_dir_object(PyObject *obj) 
{ 
    PyObject *result, *sorted; 
    PyObject *dirfunc = _PyObject_LookupSpecial(obj, &PyId___dir__); 

    assert(obj); 
    if (dirfunc == NULL) { 
     if (!PyErr_Occurred()) 
      PyErr_SetString(PyExc_TypeError, "object does not provide __dir__"); 
     return NULL; 
    } 
    /* use __dir__ */ 
    result = _PyObject_CallNoArg(dirfunc); 
    Py_DECREF(dirfunc); 
    if (result == NULL) 
     return NULL; 
    /* return sorted(result) */ 
    sorted = PySequence_List(result); 
    Py_DECREF(result); 
    if (sorted == NULL) 
     return NULL; 
    if (PyList_Sort(sorted)) { 
     Py_DECREF(sorted); 
     return NULL; 
    } 
    return sorted; 
} 

部分がに焦点を当てたです。

PyObject *dirfunc = _PyObject_LookupSpecial(obj, &PyId___dir__); 

__dir__特別な方法がルックアップされた場合に渡されたオブジェクトの上にこれがある。これは_PyObject_LookupSpecialを使用して行われます。 。 _PyObject_LookupSpecialtypeobject.cに定義されています。

PyObject * 
_PyObject_LookupSpecial(PyObject *self, _Py_Identifier *attrid) 
{ 
    PyObject *res; 

    res = _PyType_LookupId(Py_TYPE(self), attrid); 
    if (res != NULL) { 
     descrgetfunc f; 
     if ((f = Py_TYPE(res)->tp_descr_get) == NULL) 
      Py_INCREF(res); 
     else 
      res = f(res, self, (PyObject *)(Py_TYPE(self))); 
    } 
    return res; 
} 

_PyObject_LookupSpecial最初の呼び出しで渡されたオブジェクトのPy_TYPEを探してアップ_PyType_LookupIdを使用して属性を前に。オブジェクトのob_typeを取得するPy_TYPE is a macroそれが展開だな形式は次のとおりです。

(((PyObject*)(o))->ob_type) 

をそして、あなたはおそらく推測として、ob_type attribute is the class (or type) of the object:あなたは、上から見ることができるよう

typedef struct _object { 
    _PyObject_HEAD_EXTRA 
    Py_ssize_t ob_refcnt; 
    struct _typeobject *ob_type; 
} PyObject; 

だから、使用__dir__属性は、実際にオブジェクトのクラスです。

関連する問題