【问题标题】:Mutable Custom Class & Descriptor confusion可变自定义类和描述符混淆
【发布时间】:2018-02-01 23:38:33
【问题描述】:

我试图了解描述符的工作原理,尤其是与自定义可变类结合使用时。

我的问题的简洁摘要:

  • 如何创建一个自定义类,它有描述符,但会是可变的?

现在让我们添加一些上下文和内容。我最近一直在学习 Pluralsights 高级 Python 课程,他们在其中介绍了描述符。因此,我正在使用课程中的模式。但它并没有像我预期的那样工作。

Descriptor的模式:

我使用的是完全不同的,但它具有相同的模式,只是它执行的检查更复杂。但那部分是 单元测试,所以我真的不认为这是一个问题,因为 进一步的理由请继续阅读。

class DataFrameDescriptor:

    def __init__(self):
        self._instance_data = WeakKeyDictionary()

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return self._instance_data[instance]

    def __set__(self, instance, value):
        # some code which makes sure, the columns of the descriptor 
        #  stay the same
           self._instance_data[instance] = value

    def __delete__(self, instance):
        raise AttributeError("Cannot delete attribute")

描述的自定义类

这里我再次只放置了类的脚手架,以避免真正的类实现的不必要的大量代码。 基本上,我想要的是有一个自定义类,它实现了一个排序 集合的接口,而集合又由“管理/验证” 描述符。更准确地说,我正在实施 pandas.DataFrame,我对其有某些不变的设置 形状,存储某些对象,我希望我的自定义类是 像字典一样的数据框接口

from collections.abc import MutableMapping

class MutableClass(MutableMapping):
    """ All the mutable mapping required methods are implemented and work as intended """

    descriptor = DataFrameDescriptor()

    def __init__(self, some_dict):
        df = prepare(some_dict) # prepares the dict to a dataframe
        self.descriptor = df

    def __setitem__(self, key, value):
        # some checks about the value
        self.descriptor.loc[key] = value # value must be specific

现在,我得到的错误的解释:

大多数错误都围绕__hash__ 实现,这是有道理的,因为描述符字典需要键,它们是散列。 但是,据我了解,实现__hash____eq__,它们具有相同的比较结果,是一种承诺,即对象在其运行时生命周期内不会发生变异(更改)。 就我而言,这绝对不是真的。

我已经实现了__hash____eq__ 的版本,它们可以正常工作并消除错误。然而,这基本上是一个谎言,因为:

  • 我希望能够比较我的对象,比较可以告诉我它们“持有”的信息是否相同

希望我的问题足够详细且易于理解。

一个可能的改写是如何在非散列中使用描述符 自定义类。或者如何制作一个可以与 不可散列的自定义类。

【问题讨论】:

  • 对不起,我完全不明白你的问题是什么。您说“大多数错误都围绕着 hash 实现”,但在您的代码中任何地方都没有 __hash__ 方法。您的 MutableClass 无法实例化,因为您没有实现所有 MutableMapping 方法。您正在尝试将字典分配给 self.descriptor,它只接受正数。这些都没有任何意义。我认为如果你用一些代码来代替这个冗长的解释,这将有很大的帮助,这些代码可以准确地展示什么是不工作的。
  • 是的,我不清楚您的问题到底是什么。您的问题似乎根本与描述符无关,而是与可散列对象有关。
  • @Rawing 是的,我忘了说,所有这些都实现了。我应该更清楚。
  • 重点是,我不想实现散列函数,因为这些类的值会在其生命周期内发生变化。我所做的实现工作,但它只是散列了一些类不可更改的值。不完全理想。

标签: python oop dictionary descriptor


【解决方案1】:

执行此操作的通常方法是使用property 而不是自定义描述符类,并将数据存储在实例而不是描述符上:

class Mutable(object):
    def __init__(self, x):
        self.x = x
    @property
    def x(self):
        return self._x
    @x.setter(self, x):
        if x <= 0:
            raise ValueError("x must be positive")
        self._x = x

使用property,根据属性名称来选择底层实例属性的名称是很自然的;例如,我创建了一个名为 _x 的基础属性来支持名为 x 的属性,遵循在前加下划线的标准模式。

使用自定义描述符类会更尴尬,因为您不能对属性名称进行硬编码(或者至少,对属性名称进行硬编码会导致名称冲突问题)。在 Python 3.6 中更好,因为__set_name__ 让描述符知道它正在管理什么属性:

class PositiveAttr:
    def __set_name__(self, owner, name):
        self.underlying = f'_{name}'
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return getattr(instance, self.underlying)
    def __set__(self, instance, value):
        if value <= 0:
            raise ValueError("Attribute must be positive")
        setattr(instance, self.underlying, value)

class Mutable:
    x = PositiveAttr()
    ...

否则,您必须告诉描述符在其构造函数中使用什么名称:

class PositiveAttr(object):
    def __init__(self, underlying):
        self.underlying = underlying
    ...

class Mutable(object):
    x = PositiveAttr('_x')
    ...

【讨论】:

  • 答案内容丰富,名称部分不是什么大问题。我将尝试您的建议,将其作为具有复杂设置器的属性来实现。如果它适用于所有其他相关的事情,我会接受这个作为答案:)
  • 我将它实现为描述符,因为我想重用而不是复制其他类中的复杂设置器代码。所以我认为自定义描述符是正确的方法。
  • @SirSteel 你不必这样做,只需定义一个模块级函数:def _getter(self): ....def _setter(self, x): ... 然后在类定义中,使用x = property(_getter, _setter)跨度>
  • 是的,我现在正在尝试这个。可能是更好的选择。我只是希望能够在许多模块中重用设置器。
猜你喜欢
  • 2011-10-12
  • 1970-01-01
  • 2018-01-05
  • 1970-01-01
  • 2016-11-17
  • 2013-04-03
  • 1970-01-01
  • 2010-12-22
  • 1970-01-01
相关资源
最近更新 更多