Python中的描述符类设计(带继承)

Python中的描述符类设计(带继承),python,class,inheritance,descriptor,Python,Class,Inheritance,Descriptor,我试图设计一个描述符类,我可以通过另一个类使用它,这个类是一个类的子类,而这个类是一个类的子类 class MyDescriptorClass(object): def __init__(self, owner, name, activates = 0): self.value = None self.name = name self.owner = owner self.activates = 0 self

我试图设计一个描述符类,我可以通过另一个类使用它,这个类是一个类的子类,而这个类是一个类的子类

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()
将无法根据

因此,唯一的方法是将
ParentClass
中的
A=MyDescriptorClass()
定义为

class ParentClass(object):
    A = MyDescriptorClass() # here I am unable to pass the owner
由于我使用的是一个子类,
super
call跳过这一部分,直接从
\uuuuu init\uuuuuu

是否有任何方法可以修改设计,以便直接设置
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
正如他所评论的: 描述符必须是类属性,而不是实例属性才能工作- 因此,首先:

class ParentClass(object):
    A = MyDescriptorClass()
    B = MyDescriptorClass()
    def __init__(self, name):
        self.states = {}
        self.name = name
        self.A.configure(self, name, activates = 1)    
        self.B.configure(self, name, activates = 1)
        self.states.setDefault('A', self.A)
        self.states.setDefault('B', self.B)
然后相应地修复描述符类: 然后在实例本身中保留引用实例的所有数据 (这就是为什么
\uuuuu获取\uuuuu
\uu设置\uuuuu
接收对象本身)-或者 每个描述符实例都有一个字典,可以在其中注释相关数据 它们也属于类的实例,例如,通过对象ID

您的描述符类可以大致如下所示:

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

尽量不要将特定于实例的信息放在描述符中。在实例属性中保留特定于实例的信息,并在描述符中保留特定于描述符的信息(如
激活
):

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    
  • 请注意,
    \uuuuuuuuuuuuuuuu
    \uuuuuuuuu
    方法通过 正在访问描述符的实例。因此,你不需要 需要在MyDescriptor中存储
    所有者
    实例是
    
    所有者

  • 鉴于在下面的评论中对问题的澄清,下面是我将如何实现描述符

    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
    
  • 描述符必须定义为类属性,而不是实例 属性。由于描述符由所有实例访问,因此 可能不希望仅仅因为 正在创建实例。因此,不要实例化
    \uuuu init\uuuu
    中的描述符
  • Gate的每个实例都有一个输入列表。这些项目是自我输入的 是常量或门的实例
  • 这里我们看到常量类的用途。对于每个
    gate.ans
    需要返回一个值
  • 索引记录了仪表输入中的哪个项目
    GateInput
    是 连接到
  • inst是Gate的一个实例。例如,
    c.A
    导致Python 调用
    GateInput.\uuuu获取(self,c,type(c))
    。因此,
    inst
    c
    在这里

  • 所以你需要所有者是
    父类而不是绑定到的类?我不确定我是否理解你的问题;不,您不能将描述符设置为实例属性,它们必须驻留在类中。但我不清楚您试图用描述符实现什么。子类将从其父类层次结构继承描述符,因此
    self。如果
    A
    是类属性,则
    ChildClass2
    上将提供A
    。您的描述符直接在描述符实例上设置值;您可能想在所有者实例上设置它们。我正在尝试这个。但它将
    self.A
    显示为
    NoneType
    对象,并抛出
    AttributeError
    这很好。但是,我无法使用
    connect
    。c=儿童类别1(“c1”);b=儿童2级(“b1”);c、 A.连接(b.A);这会给出
    AttributeError
    ,即
    NoneType
    对象没有属性
    connect
    是否希望
    connects
    列表与实例
    c
    关联(即每个实例是否有不同的
    connects
    列表),或者您想让所有的
    父类
    实例潜在地改变相同的
    连接
    列表吗?
    连接
    列表与
    MyDescriptClass
    有什么关系?它在那里干什么?将
    connect
    方法放在
    ParentClass
    中不是更有意义吗?我正在制作类似逻辑电路的东西。因此
    ParentClass
    这里是
    Gate
    ,有2个输入值和1个输出值
    和Gate
    将是这个类的子类。而
    描述符class
    用于连接。因此,如果门1的输出转到门2的输入A。