[英]Descriptor class design in Python (with inheritance)
I'm trying to design a descriptor class which I can use through other class which is a subclass of a class which is a subclass of a class. 我正在尝试设计一个描述符类,我可以通过其他类使用该描述符类,该其他类是一个类的子类,而该类是一个类的子类。
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()
will not work according to the python docs 根据python文档
self.A = MyDescriptorClass()
将不起作用
so the only way is that I declate A = MyDescriptorClass()
in the ParentClass
as 所以唯一的方法是我将
ParentClass
中的A = MyDescriptorClass()
为
class ParentClass(object):
A = MyDescriptorClass() # here I am unable to pass the owner
And since, I'm using a child class, super
call skips this part and starts directly with __init__
并且由于我使用的是子类,因此
super
call跳过了这一部分,直接以__init__
开始
Is there any way in which I can modify the design so as to set the value of ChildClass1.A
instance directly? 有什么方法可以修改设计以直接设置
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
Try not to put information which is specific to instances in the descriptor. 尽量不要将特定于实例的信息放在描述符中。 Keep information specific to instances in instance attributes, and keep information specific to the descriptor (like
activates
) in the descriptor: 将特定于实例的信息保留在实例属性中,并将特定于描述符的信息保留在描述符中(例如
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
__set__
and __get__
methods are passed the instance
which is accessing the descriptor. __set__
和__get__
方法传递给正在访问描述符的instance
。 Therefore, you do not need to store the owner
in MyDescriptor. owner
存储在MyDescriptor中。 The instance
is the owner
. instance
是owner
。 Given the clarification of the problem in the comments below, here is how I would implement the descriptor. 鉴于以下注释中对问题的澄清,这就是我将如何实现描述符。
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
__init__
. __init__
实例化描述符。 gate
, gate.ans
needs to return a value. gate
, gate.ans
需要返回一个值。 GateInput
is connected to. GateInput
连接到inst.inputs中的哪个项目。 cA
causes Python to call GateInput.__get__(self, c, type(c))
. cA
使Python调用GateInput.__get__(self, c, type(c))
。 Thus, inst
is c
here. inst
是c
。 As it is int he comments: descriptors must be class attributes, not instance attributes in order to work - so, to start with: 正如他所说的那样:描述符必须是类属性,而不是实例属性才能起作用-因此,从以下内容开始:
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)
And then you fix your Descriptor class accordingly: either have then keeping all data refering to an instance in the instance itself (that is why __get__
and __set__
receive the object itself) - or have each descriptor instance have a dictionary where they can annotate data related to the instances of the class they belong too, by, for example, object ID. 然后相应地修复Descriptor类:要么保留所有数据引用实例本身中的一个实例(这就是
__get__
和__set__
接收对象本身的原因),要么让每个描述符实例都有一个字典,在其中可以注释与数据相关的数据它们也属于类的实例,例如通过对象ID。
Your descriptor class could be more or less along these lines: 您的描述符类或多或少地遵循以下原则:
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
...
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.