2014-02-14 6 views
1

Я пытаюсь создать класс дескриптора, который я могу использовать через другой класс, который является подклассом класса, который является подклассом класса.Дизайн класса дескриптора в Python (с наследованием)

class MyDescriptorClass(object): 
    def __init__(self, owner, name, activates = 0): 
     self.value = None 
     self.name = name 
     self.owner = owner 
     self.activates = 0 
     self.connects = [] 

    def __set__(self, obj, val): 
     self.set(val) 
    def __get__(self, instance, owner): 
     return self.value 

    def set(self, value): 
     if self.value == value: 
      return 0 
     self.value = value 
     if self.activates: 
      self.owner.evaluate() 

    def connect(self, inputs): 
     if not isinstance(inputs, list): 
      inputs = list(inputs) 
     for input in inputs: 
      self.connects.append(input) 

class ParentClass(object): 
    def __init__(self, name): 
     self.states = {} 
     self.name = name 
     self.A = MyDescriptorClass(self, name, activates = 1)  
     self.B = MyDescriptorClass(self, name, activates = 1) 
     self.states.setDefault('A', self.A) 
     self.states.setDefault('B', self.B) 

class ChildClass1(ParentClass): 
    def __init__(self, name) 
     super(ChildClass1, self).__init__(name) 
     self.ans = None 
    def evaluate(self): 
     self.ans = self.A.value + self.B.value 

class ChildClass2(ParentClass): 
    def __init__(self, name) 
     super(ChildClass1, self).__init__(name) 
     self.ans = None 
    def evaluate(self): 
     self.ans = self.A.value * self.B.value 

self.A = MyDescriptorClass() не будет работать в соответствии с python docs

так что единственный способ, что я declate A = MyDescriptorClass() в ParentClass в

class ParentClass(object): 
    A = MyDescriptorClass() # here I am unable to pass the owner 

И так, я использую класс ребенка, super вызов пропускает эту часть и начинается с __init__

Есть ли способ, в котором я могу изменить дизайн, чтобы напрямую установить значение ChildClass1.A экземпляра?

c = ChildClass1("c1") 
    c.A = 10 # I directly want to set this value instead of using c.A.set(10) 
    c.B = 20 
    c.evaluate() 
    print c.ans # 30 
    c.B = 40 
    print c.ans # 50 
+0

Значит, вам нужен владелец, чтобы быть «ParentClass» вместо того, чтобы связать класс? –

+1

Я не уверен, что следую вашему вопросу; нет, вы не можете установить дескрипторы в качестве атрибутов экземпляра, они должны находиться в классе. Но я не понимаю, чего вы пытаетесь достичь с помощью вашего дескриптора. –

+0

Подкласс будет наследовать дескрипторы из иерархии их родительского класса, поэтому 'self.A' будет доступен в' ChildClass2', если 'A' является атрибутом класса. –

ответ

0

Как это ИНТ он комментирует: дескрипторы должны быть атрибуты класса, не атрибуты экземпляра для того, чтобы работать - так, чтобы начать с:

класс ParentClass (объект): A = MyDescriptorClass() В = MyDescriptorClass() Защита INIT (само, имя): self.states = {} self.name = имя self.A.configure (сам, имя, активирует = 1)
self.B.configure (само, имя, активирует = 1) self.states.setDefault ('A', self.a) self.states.setDefault ('B', self.B)

И то вы соответствующим образом определяете свой класс дескриптора: либо сохраняют все данные, ссылающиеся на экземпляр в самом экземпляре (поэтому __get__ и __set__ получают сам объект) - или имеют каждый экземпляр дескриптора имеет словарь, в котором они могут комментировать связанные с данными экземплярам класса, к которым они принадлежат, например, идентификатором объекта.

Ваш класс дескриптор может быть более или менее вдоль этих линий:

class MyDescriptorClass(object): 
    def __init__(self): 
     self.data = defaultDict(dict) 
    def configure(self, owner, name, activates = 0): 
     container = self.data(id(owner)) 
     container["value"] = None 
     container["name"] = name 
     ... 

    def __set__(self, owner, value): 
     # implemnt your previous "set" method straight here 
     ... 
     ... 
+0

Я пытался это сделать. но он показывает 'self.A' как объект« NoneType »и выбрасывает« AttributeError » – Froyo

2

Старайтесь не размещать информацию, которая является специфичным для экземпляров в дескрипторе. Хранить информацию специфичны для случаев, атрибутов экземпляра и хранить информацию специфичны для дескриптора (например, activates) в дескрипторе:

class MyDescriptorClass(object): 
    def __init__(self, activates = 0): 
     self.value = None 
     self.activates = activates 
     self.connects = [] 

    def __set__(self, instance, val):  # 1 
     if self.value == val: 
      return 0 
     self.value = val 
     if self.activates: 
      instance.evaluate() 
    def __get__(self, instance, instcls): # 1 
     return self.value  
  1. Обратите внимание, что __set__ и __get__ методы Сдал instance который обращается дескриптор , Поэтому вам не нужно необходимо сохранить owner в MyDescriptor. instance - owner.

Учитывая разъяснения проблемы в комментариях ниже, вот как я бы реализовать дескриптор.

class GateInput(object): 
    def __init__(self, index): 
     self.index = index     # 4 

    def __get__(self, inst, instcls): 
     return inst.inputs[self.index].ans # 5 

    def __set__(self, inst, val): 
     if isinstance(val, (float, int)): 
      inst.inputs[self.index] = Constant(val) 
     else: 
      inst.inputs[self.index] = val 


class Constant(object): 
    def __init__(self, val): 
     self.ans = val 


class Gate(object): 
    A = GateInput(0)    # 1 
    B = GateInput(1)    # 1 

    def __init__(self, name): 
     self.name = name 
     self.inputs = [Constant(0), Constant(0)] # 2 


class Adder(Gate): 
    @property 
    def ans(self): 
     result = 0 
     for gate in self.inputs: 
      result += gate.ans      # 3 
     return result 


class Multiplier(Gate): 
    @property 
    def ans(self): 
     result = 1 
     for gate in self.inputs: 
      result *= gate.ans 
     return result 

b = Multiplier('b1') 
b.A = 2 
b.B = 3 
print(b.A) 
# 2 
print(b.ans) 
# 6 

c = Adder('c1') 
c.A = 10 
print(c.ans) 
# 10 

# This connects output of b to an input of c 
c.B = b 
print(c.ans) 
# 16 
  1. дескрипторы должны быть определены как атрибуты класса, а не экземпляра атрибутов. Поскольку доступ к дескриптору осуществляется с помощью , все экземпляров, вы, возможно, не хотите, чтобы дескриптор изменялся только потому, что создается экземпляр . Поэтому не создавайте экземпляр дескриптора в __init__.
  2. Каждый экземпляр Gate имеет список входов. Элементы self.inputs являются экземплярами Constant или Gate.
  3. Здесь мы видим цель класса Constant. Для каждого gate, gate.ans необходимо вернуть значение.
  4. Индекс записывает, какой элемент в inst.inputs GateInput имеет значение .
  5. inst - это экземпляр Gate. Например, c.A заставляет Python позвонить GateInput.__get__(self, c, type(c)). Таким образом, inst является c здесь.
+0

Это отлично работает. Однако я не могу использовать 'connect'. c = ChildClass1 ('c1'); b = ChildClass2 ('b1'); c.A.connect (b.A); Это дает 'AttributeError', что объект' NoneType' не имеет атрибута 'connect' – Froyo

+0

Вы хотите, чтобы список' connects' ассоциировался с экземпляром 'c' (т.е. должен ли быть другой список' connects' для каждого экземпляр), или вы хотите, чтобы все экземпляры «ParentClass» потенциально изменяли один и тот же список «connects»? – unutbu

+0

Что делает список 'connects' связан с' MyDescriptClass'? Что он там делает? Разве не было бы смысла вкладывать метод 'connect' в' ParentClass'? – unutbu