可变定制类&描述符混淆

时间:2018-02-01 23:38:34

标签: python oop dictionary descriptor

我试图理解描述符的确切原理,特别是与自定义可变类一起使用。

我的问题的希望简洁摘要:

  • 我如何创建一个自定义类,它将具有描述符,但是可以变化?

现在让我们添加一些背景和内容。我最近一直在做Pluralsights高级Python课程,他们在那里介绍了描述符。因此,我正在使用课程中的模式。但它不像我预期的那样有效。

描述符的模式:

  

我使用的那个是完全不同的,但它具有相同的模式,只是它执行的检查更复杂。但那部分是   单位测试,所以我真的不希望这是一个问题,因为   进一步说明理由。

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__实现,这是有道理的,因为描述符dict需要键,即哈希。 但是,据我所知,实现__hash____eq__,它们具有相同的比较结果,是一种承诺,即对象不会在其运行时生命周期内发生变化(更改)。 在我的情况下,这绝对不是真的。

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

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

希望我的问题足够详细且可以理解。

  

潜在的改述是如何在不可清除的情况下使用描述符   自定义类。或者如何制作一个可以使用的描述符   不可清洗的自定义类。

1 个答案:

答案 0 :(得分: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 = Positive()
    ...

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

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

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