2015-01-23 2 views
1

Может ли кто-нибудь сказать мне, почему, когда я нажимаю кнопку тестирования, __get__ не называется? Я пробовал много вещей, но я не могу достичь желаемого поведения:дескрипторы Python3 не вызывают __get__ при доступе

когда lt или gt атрибута доступен значение соответствующего 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_()) 

ответ

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().

Если вы не хотите, чтобы изменить или удалить setattr() вызов, вы всегда можете вручную установить атрибут экземпляра:

self.__dict__[name] = attribute 

Это обходит дескриптор данных, и делает именно то, что setattr() вызов в настоящее время делает. Однако атрибут, вероятно, не будет соответствовать возвращаемому значению __get__(). На мой взгляд, это плохой дизайн, но это не строго неправильный.

+0

спасибо, Kevin. > По-моему, это плохой дизайн, но это не совсем неправильно. Это действительно важно для меня: делать это определенно правильно. вещи, которую я хочу достичь в том, что: У меня есть фильтр объектов и много таких селекторов , которые я хочу ответить так: они имеют некоторый ATTRS и каждый из этой ATTRS ответить так: при создании селектора - есть одно поле, добавленное в макет селекторов для каждого attr такого рода, который у него есть. , когда attr acceded: если selectors checkbox проверял значение этого поля attr, возвращаемого. Каким образом это может быть достигнуто для правильного проектирования? –

+0

Прошу прощения, но Qt не является моей областью знаний. Большая часть вашего комментария для меня такова. Что вы пытались сделать с этим вызовом 'setattr()'? На что вы надеетесь, что это будет достигнуто? – Kevin

+0

Чтобы предоставить возможность esily создавать декларативные подклассы. Подкласса есть, например: класса SomeClass (CheckSelector): attr1 = LineDescriptor() attr2 = LineDescriptor() Что мне нужно: все экземпляры SomeClass имеет свойство с некоторым поведением. И некоторые шаги необходимо выполнить на этапе .__ init__ шаг для каждого из таких свойств. setattr необходимо только установить свойство в экземпляр –

 Смежные вопросы

  • Нет связанных вопросов^_^