Python3描述符在访问时不会调用__get__

时间:2015-01-23 19:01:42

标签: python-3.x descriptor

任何人都可以告诉我为什么当我按下测试按钮时,__get__没有被调用?我尝试了很多东西,但我无法达到预期的行为:

  

当访问ltgt属性时,如果选中复选框,则必须返回相应QlineEdit的值

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QVBoxLayout, QRadioButton, QGroupBox, QLineEdit, QCheckBox, QGridLayout, QLabel, QFrame, QPushButton
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIntValidator
from PyQt5.QtWidgets import QMainWindow


class LineDescriptor:
    def __init__(self, row=1, label=None):

        self.label = label
        self.row = row
        self.__name = None

    def __get__(self, instance, owner=None):
        print('get_called')
        if instance is None:
            print('is none')
            return self
        else:
            private_name = '_{0}__{1}'.format(type(instance).__name__,
                                              self.__name)
            check_name = '_{0}__{1}'.format(type(instance).__name__,
                                            self.__name)
            if getattr(instance, check_name).isChecked():
                return getattr(instance, private_name).text()
            else:
                return None


class CheckSelector(QGroupBox):
    def __init__(self, name):
        self.__dictionary = []
        super(CheckSelector, self).__init__()
        layout = QGridLayout()
        layout.setAlignment(Qt.AlignTop)

        name = QLabel(name)
        layout.addWidget(name, 0, 0, 1, 1)

        self.__check = QCheckBox()
        layout.addWidget(self.__check, 0, 1, 1, 1)
        self.setLayout(layout)

        for name, attribute in type(self).__dict__.items():
            if isinstance(attribute, LineDescriptor):
                row = attribute.row
                if attribute.label is not None:
                    label = QLabel(attribute.label)
                    self.layout().addWidget(label, row, 0, 1, 1)
                int_validator = QIntValidator()
                field = QLineEdit()
                field.setValidator(int_validator)
                layout.addWidget(field, row, 1, 1, 1)
                private_name = '_{0}__{1}'.format(type(self).__name__, name)

                setattr(attribute, '_LineDescriptor__name', name)
                setattr(self, name, attribute)
                setattr(self, private_name, field)
                print(attribute)
                print(attribute.__dict__)





class GtCheck(CheckSelector):
    gt = LineDescriptor(1, '>')


class BothCheck(CheckSelector):
    gt = LineDescriptor(1, '>')
    lt = LineDescriptor(2, '<')


def checker():
    print(test.lt)
app = QApplication(sys.argv)

win = QMainWindow()
wid = QFrame()
lay = QVBoxLayout()
test = BothCheck(name='слов')
lay.addWidget(test)
but = QPushButton()
but.clicked.connect(checker)
lay.addWidget(but)
wid.setLayout(lay)
win.setCentralWidget(wid)
win.show()
sys.exit(app.exec_())

1 个答案:

答案 0 :(得分:0)

这是违规行:

setattr(self, name, attribute)

您的描述符是非数据描述符(它不实现__set__())。因此,这会在self.__dict__中设置属性,从而创建实例属性。实例属性优先于非数据描述符。如果您在下一行立即执行此操作:

getattr(self, name)

...然后不会调用描述符;它只会从attribute中取回self.__dict__并直接返回。由于foo.bar getattr(foo, "bar")完全相同,因此foo.bar语法也不会调用描述符。

数据描述符(实现__set__()的描述符)优先于实例属性。如果您更改LineDescriptor以实施__set__()以及__get__(),则不会出现此行为。但是你必须弄清楚如何实现__set__()。该实现将取决于您要完成的任务:

  1. 您可以引发AttributeError以指示描述符是只读的。然后你必须删除setattr()调用,因为你不能设置只读描述符。
  2. 您可以实施__set__()来执行与__get__()相反的操作。鉴于__get__()方法的复杂性,我不完全确定如何做到这一点。它还会改变setattr()来电的效果。
  3. 如果您不想更改或删除setattr()电话,您可以随时手动设置实例属性:

    self.__dict__[name] = attribute
    

    这会绕过数据描述符,并完全执行setattr()调用当前正在执行的操作。但是,该属性可能与__get__()的返回值不匹配。在我看来,这是糟糕的设计,但并不严格错误