为什么 getattr(self, "x") 与本例中的 self.x 不同?

Why is getattr(self, "x") not the same as self.x in this example?

我发现 getattr(self, "x") 的行为与 self.x 不同的情况。抱歉 post 的长度 - 我不知道如何在没有 PySide2 的情况下重现该行为。所以有了这个 example.qml 文件:

import QtQuick 2.10
import QtQuick.Controls 2.1
import QtQuick.Window 2.2
import QtQuick.Controls.Material 2.3

ApplicationWindow {
    id: applicationWindow
    Material.theme: Material.Dark
    title: qsTr("Test Invoke")
    visible: true

    width: 600
    height: 500

    Slider {
        id: slider1
        x: 69
        y: 215
        value: Manager.xyz
        property bool updateValueWhileDragging: true
        onMoved: Manager.xyz = value
    }
}

和这个 example.py 文件:

import sys
import os

from PySide2.QtCore import Qt, QObject, Signal, Slot, Property
from PySide2.QtGui import QGuiApplication
from PySide2.QtQml import QQmlApplicationEngine
from PySide2.QtWidgets import QSlider, QPushButton, QCheckBox

class Manager(QObject):
    xyz_slot = Signal()
    def __init__(self):
        QObject.__init__(self)
        self.__xyz = None
        self.xyz_slot.connect(self.on_xyz_changed)
        self.xyz = 0.3

    @Property(float, notify=xyz_slot) # (6)
    def xyz(self):
        # (1) This works
        # return self.__xyz
        # (2) This doesn't work and produces the error:
        # Error: 'Manager' object has no attribute '__xyz'
        return getattr(self, "__xyz")

    @xyz.setter
    def set_xyz(self, val):
        if self.__xyz == val:
            return
        self.__xyz = val
        self.xyz_slot.emit()

    @Slot()
    def on_xyz_changed(self):
        print(self.__xyz)

if __name__ == "__main__":
    os.environ["QT_QUICK_CONTROLS_STYLE"] = "Material"
    app = QGuiApplication(sys.argv)
    engine = QQmlApplicationEngine()
    manager = Manager()
    ctx = engine.rootContext()
    ctx.setContextProperty("Manager", manager)
    engine.load('example.qml')
    if not engine.rootObjects():
        sys.exit(-1)
    sys.exit(app.exec_())

使用 Python 3.7.

为什么 getattr (2) 版本不工作?

请注意,我需要一个带 getattr 的版本,因为我想装饰其他 类。在那种情况下,不可能使用点调用版本。 在the documentation中,有一个例子声称两者是一样的。我可以想象可能是 PySide2 的 C++ 代码导致了差异。但是我不知道如何从这里开始。

因为您正在使用双下划线名称修改。

来自the docs

Notice that code passed to exec() or eval() does not consider the classname of the invoking class to be the current class; this is similar to the effect of the global statement, the effect of which is likewise restricted to code that is byte-compiled together. The same restriction applies to getattr(), setattr() and delattr(), as well as when referencing __dict__ directly.

您可能不需要使用双下划线名称修饰,所以不要使用它。