Python:描述符列表

Posted

技术标签:

【中文标题】Python:描述符列表【英文标题】:Python: A List of Descriptors 【发布时间】:2012-11-06 23:23:15 【问题描述】:

我正在尝试使用列表来引用一系列描述符,但没有成功。我想通过描述符 (class Descriptor) 访问由外部库 (class A) 定义的对象列表 (_b)。在下面的示例中,b 被分配了对描述符的引用列表,但是当列表中的任何项目被分配一个值时,对描述符的引用被该值覆盖,而不是将该值传递给描述符。即使在阅读了几篇关于描述符的参考资料和文章之后,我也显然错过了描述符的基本行为。

class Descriptor(object):
    def __init__(self, varname):
        self.varname = varname
        pass

    def __get__(self, instance, owner):
        print('get', self.varname)
        #return getattr(getattr(instance, self.varname),"get")()
        return instance.__dict__[self.varname].get()

    def __set__(self, instance, value):
        print('set', self.varname)
        #getattr(getattr(instance, self.varname),"set")(value)
        instance.__dict__[self.varname].set(value)

class A(object):
    def __init__(self, value=None):
        self.value = value
    def get(self):
        return self.value
    def set(self, value):
        self.value = value

class C(object):
    print "root"
    a = Descriptor('_a')
    b = [Descriptor('_b[x]') for x in range(5)]
    def __init__(self, val):
        print "init"
        self._a = A()
        self.a = val
        self._b = [A() for x in range(5)]
        self.b[0] = 1
c = C(3)
d = C(4)

print c._a.get()
print c.a
print d._a.get()
print d.a
print c.b[0]

在我的实际程序中,外部库是一个 gui 库,我想对其进行抽象,以便可以轻松地交换不同的接口。 gui 中的几个视图包含与程序中的列表相对应的条目框列(每列最多 40 个)。

另外,这是访问传递给描述符的实例对象的成员函数的首选方法:getattr__dict____dict__ 看起来更干净,但我不知道在使用它时是否存在任何框架或可用性问题。

对于所提出的问题的任何帮助或满足我在该计划中的需求的其他方法的建议,我们将不胜感激。谢谢。

根据millimoose 的建议,以下类似列表的课程似乎满足了我的需求。除了在类根中定义描述符而在类__init__ 中定义“列表描述符”以及在初始化时必须将类作为参数提供之外,此方法是否存在任何缺陷?需要添加其他列表功能,需要添加负索引等特殊索引行为。

class DescriptorList(object):
    def __init__(self, owner, varname):
        self.owner = owner
        self.varname = varname

    def __getitem__(self, index):
        print('getitem', self.varname, index)
        return getattr(getattr(self.owner, self.varname)[index],"get")()

    def __setitem__(self, index, value):
        print('setitem', self.varname, index)
        getattr(getattr(self.owner, self.varname)[index],"set")(value)

class C(object):
    a = Descriptor('_a')
    def __init__(self, val):
        self._a = A()
        self.a = val
        self._b = [A() for x in range(5)]
        self.b = DescriptorList(self, '_b')
        for i in range(5):
            self.b[i] = i

c = C(3)
print [c.b[i] for i in range(5)]

此外,DescriptorListC.__init__ 中被实例化,代码可以被简化,以便DescriptorList 使用对象本身而不是对象的名称。这种方法有什么优点或缺点吗?

class DescriptorList(object):
    def __init__(self, var):
        self.var = var

    def __getitem__(self, index):
        print('get', self.var, index)
        return self.var[index].get()

    def __setitem__(self, index, value):
        print('set', self.var, index)
        self.var[index].set(value)

class C(object):
    a = Descriptor('_a')
    def __init__(self, val):
        self._a = A()
        self.a = val
        self._b = [A() for x in range(5)]
        self.b = DescriptorList(self._b)
        for i in range(5):
            self.b[i] = i

为什么__get____set____getitem____setitem__ 的处理方式不同?

【问题讨论】:

对于使用__slots__namedtuples 的类,访问__dict__ 将失败,谁知道还有什么。基本上,不保证任意 Python 对象具有__dict__ 我很难弄清楚您的期望:b = [Descriptor('_b[x]') for x in range(5)] 要做。描述符仅在直接绑定到类时才起作用,而不是放在绑定到类的列表中。即使这确实有效,您通常也不会将变量命名为 _b[x] 另外,对于b,您不需要描述符。您需要一个类似列表的对象,它委托给A 实例的适当方法,或创建它们等。 @mgilson 我认为他希望c.b[0] = 123 最终调用与b[0] 的包装对象相对应的方法A.set()。 (这当然不能用描述符来完成。) @millimoose 感谢__dict__ 上的信息。使用getattr 有什么缺点或注意事项吗? 【参考方案1】:

我需要类似的东西,我想出了以下内容:

class Element(object):
  def __get__(self, obj, objtype=None):
    print("Element.__get__(), obj.idx=0".format(obj.idx))
    return random.randint(1, 10)

  def __set__(self, obj, value):
    print("Element.__set__(), obj.idx=0, value=1".format(obj.idx, value))

class Indexable(object):
  # Can also sub-class from list; I needed mine to be fixed length 
  # because I was mapping them to a set of hardware registers.

  element = Element()

  def __init__(self, sz):
    self.sz = sz
    self.idx = 0

  def __getitem__(self, idx):
    self.idx = idx
    return self.element

  def __setitem__(self, idx, value):
    self.idx = idx
    self.element = element

class Owner(object):
  def __init__(self):
    self.seq = Indexable(5)

Element.__get__ 调用 random.randint() 但显然,这是任意的。我需要的是索引,以便 get/set 函数知道要读取/写入哪个寄存器。

【讨论】:

【参考方案2】:

根据来自 millimoose 和 another post 本质上相似的反馈,以下代码似乎可以满足我的需求,尽管它看起来有点杂乱无章。

class Descriptor(object):
    def __init__(self, varname, index=None):
        self.varname = varname
        self.index = index

    def __get__(self, instance, owner):
        print('get', self.varname, self.index)
        if self.index is not None:
            return getattr(getattr(instance, self.varname)[self.index],"get")()
        else:
            return getattr(getattr(instance, self.varname),"get")()

    def __set__(self, instance, value):
        print('set', self.varname, self.index)
        if self.index is not None:
            getattr(getattr(instance, self.varname)[self.index],"set")(value)
        else:
            getattr(getattr(instance, self.varname),"set")(value)

class DescriptorList(list):
    def __init__(self, initlist, instance):
        self.instance = instance
        super(DescriptorList,self).__init__(initlist)

    def __getitem__(self, index):
        print('getitem', self.instance, index)
        return super(DescriptorList,self).__getitem__(index).__get__(self.instance, self.instance.__class__)

    def __setitem__(self, index, value):
        print('setitem', self.instance, index, value)
        super(DescriptorList,self).__getitem__(index).__set__(self.instance, value)

class A(object):
    def __init__(self, value=None):
        self.value = value
    def get(self):
        return self.value
    def set(self, value):
        self.value = value

class C(object):
    a = Descriptor('_a')
    b = [Descriptor('_b', x) for x in range(5)]

    def __init__(self, val):
        self._a = A()
        self.a = val
        self._b = [A() for x in range(5)]
        self.b = DescriptorList(self.__class__.b, self)
        for i in range(5):
            self.b[i] = i*val

c = C(3)
d = C(4)
print c.a
print d.a
print [c.b[i] for i in range(5)]
print [d.b[i] for i in range(5)]

感谢任何有关此解决方案的限制或错误或建议的改进。

【讨论】:

以上是关于Python:描述符列表的主要内容,如果未能解决你的问题,请参考以下文章

带有caffe的python中的VGG人脸描述符

Python描述符(descriptor)解密

Python描述符(descriptor)解密(转)

python2.7高级编程 笔记二(Python中的描述符)

将一个图像中的 SURF 描述符与其他图像中的描述符列表进行比较

python_如何将多个小字符拼接成大字符?