我试图理解描述符的确切原理,特别是与自定义可变类一起使用。
我的问题的希望简洁摘要:
现在让我们添加一些背景和内容。我最近一直在做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__
的版本,这使得它可以正常工作,并删除了错误。然而,这基本上是一个谎言,因为:
希望我的问题足够详细且可以理解。
潜在的改述是如何在不可清除的情况下使用描述符 自定义类。或者如何制作一个可以使用的描述符 不可清洗的自定义类。
答案 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')
...