2017-07-10 10 views
1

プロパティを持つ通常のPythonクラスを作成し、そのプロパティの実装でAttributeErrorにつながる間違いをします。次のようにMVCEがある:QObjectから派生したクラスのプロパティでAttributeErrorが発生したときの誤解を招くエラーメッセージ

class MyClass(): 

    @property 
    def myProp(self): 
     raise AttributeError("my mistake") 

def main(): 
    # Gives a 'expected-error-message' as expected 
    myObject = MyClass() 
    print("Regular object property: {}".format(myObject.myProp)) 


if __name__ == "__main__": 
    main() 

予想通りこれは、次のエラーを与える:

Traceback (most recent call last): 
    File "prop_regular.py", line 14, in <module> 
    main() 
    File "prop_regular.py", line 10, in main 
    print("Regular object property: {}".format(myObject.myProp)) 
    File "prop_regular.py", line 5, in myProp 
    raise AttributeError("my mistake") 
AttributeError: my mistake 

私はQObjectをからクラスを継承してみましょうしかし、もし、エラーが紛らわしいです。たとえば、次のコードを実行している

from PyQt5 import QtCore 

class MyQtClass(QtCore.QObject): 

    @property 
    def myProp(self): 
     raise AttributeError("my-mistake") 


def main(): 
    app = QtCore.QCoreApplication([]) 

    # Gives confusing error message: 'MyQtClass' object has no attribute 'myProp' 
    qc = MyQtClass() 
    print("Qt object property: {}".format(qc.myProp)) 


if __name__ == "__main__": 
    main() 

Traceback (most recent call last): 
    File "prop_qt.py", line 19, in <module> 
    main() 
    File "prop_qt.py", line 15, in main 
    print("Qt object property: {}".format(qc.myProp)) 
AttributeError: 'MyQtClass' object has no attribute 'myProp' 

を与えるしかしMyQtClassクラスmyProp性質を持っている、それだけでバグが含まれています!これは、実際のアプリケーションでデバッグするのにはしばらく時間がかかりました。

私の質問は:ここで何が起こっているのですか?これはPyQtのバグですか?または私は何か間違っているのですか?

EDIT:

Ekhumoroの答えはPyQtは(5.6)のソースで見て私を促しました。その名前のない方法はQtのメタ型システムで見つからない場合は、そのエラーメッセージが発生した以下の機能

// See if we can find an attribute in the Qt meta-type system. This is 
// primarily to support access to JavaScript (e.g. QQuickItem) so we don't 
// support overloads. 
PyObject *qpycore_qobject_getattr(const QObject *qobj, PyObject *py_qobj, 
     const char *name) 
{ 
    const QMetaObject *mo = qobj->metaObject(); 

    // Try and find a method with the name. 
    QMetaMethod method; 
    int method_index = -1; 

    // Count down to allow overrides (assuming they are possible). 
    for (int m = mo->methodCount() - 1; m >= 0; --m) 
    { 
     method = mo->method(m); 

     if (method.methodType() == QMetaMethod::Constructor) 
      continue; 

     // Get the method name. 
     QByteArray mname(method.methodSignature()); 
     int idx = mname.indexOf('('); 

     if (idx >= 0) 
      mname.truncate(idx); 

     if (mname == name) 
     { 
      method_index = m; 
      break; 
     } 
    } 

    if (method_index >= 0) 
    { 
     // Get the value to return. Note that this is recreated each time. We 
     // could put a descriptor in the type dictionary to satisfy the request 
     // in future but the typical use case is getting a value from a C++ 
     // proxy (e.g. QDeclarativeItem) and we can't assume that what is being 
     // proxied is the same each time. 
     if (method.methodType() == QMetaMethod::Signal) 
     { 
      // We need to keep explicit references to the unbound signals 
      // (because we don't use the type dictionary to do so) because they 
      // own the parsed signature which may be needed by a PyQtSlotProxy 
      // at some point. 
      typedef QHash<QByteArray, PyObject *> SignalHash; 

      static SignalHash *sig_hash = 0; 

      // For crappy compilers. 
      if (!sig_hash) 
       sig_hash = new SignalHash; 

      PyObject *sig_obj; 

      QByteArray sig_str = method.methodSignature(); 

      SignalHash::const_iterator it = sig_hash->find(sig_str); 

      if (it == sig_hash->end()) 
      { 
       sig_obj = (PyObject *)qpycore_pyqtSignal_New(
         sig_str.constData()); 

       if (!sig_obj) 
        return 0; 

       sig_hash->insert(sig_str, sig_obj); 
      } 
      else 
      { 
       sig_obj = it.value(); 
      } 

      return qpycore_pyqtBoundSignal_New((qpycore_pyqtSignal *)sig_obj, 
        py_qobj, const_cast<QObject *>(qobj)); 
     } 

     // Respect the 'private' nature of __ names. 
     if (name[0] != '_' || name[1] != '_') 
     { 
      QByteArray py_name(Py_TYPE(py_qobj)->tp_name); 
      py_name.append('.'); 
      py_name.append(name); 

      return qpycore_pyqtMethodProxy_New(const_cast<QObject *>(qobj), 
        method_index, py_name); 
     } 
    } 

    // Replicate the standard Python exception. 
    PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", 
      Py_TYPE(py_qobj)->tp_name, name); 

    return 0; 
} 

を定義すると、エラーがQtCore/qpycore_qobject_getattr.cppに由来すると思われます。私は実際に何か他のことをするのは難しいだろうと思う。

+0

なぜ私が与えた答えにあなたが反応しなかったのか知りたいのです。それはあなたの質問のすべてのポイントを扱うことに同意しますか? – ekhumoro

+0

うん。私は忙しかったので、答えを知らずに仕事を続けることができたので、それは私の心を失った。フォローアップをありがとう。 – titusjan

答えて

1

これは、AttributeErrorが上昇するときはいつでも、それは呼び出さなければならないのでクラスは、__getattr__を定義する際normal python behaviourある:

>>> class MyClass(): 
...  @property 
...  def myProp(self): 
...   raise AttributeError("my mistake") 
...  def __getattr__(self, name): 
...   raise AttributeError("no attribute %r" % name) 
... 
>>> x = MyClass() 
>>> x.myProp 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 6, in __getattr__ 
AttributeError: no attribute 'myProp' 

定義され__getattr__がない場合、元の例外が伝播されます。さもなければ、元のものが飲み込まれ、__getattr__で発生した例外が代わりに伝播されます。

QObjectから派生したすべてのPyQtクラスが__getattr__を定義しなければならないことを意味します。

+1

あなたが正しいと思う、 'QObject .__ getattr__'はPyQtソースで定義されているようです。私の編集を参照してください。 – titusjan