这是将Python @property作为具有特定条件的列表的清洁方法



现在我有以上源代码:

class Stats(object):
    def __init__(self):
        self._pending = []
        self._done = []
    @property
    def pending(self):
        return self._pending

这些列表的填充方式对我的问题并不重要。

情况是,我以这种方式获得了这些列表的冠军:

stats = Stats()
// code to fill the lists
stats.pending[2:10]

这里的问题是,我希望获得与我检索到的元素一样多的元素。在上面的示例中,我期望一个包含8个元素(10-2)的sublist。

当然,实际上,如果列表较短,我将获得少于8个元素。

所以,我需要的是:

  • 当列表有足够的项目时,它将返回相应的sublist。
  • 当列表较短时,它将返回一个带有预期长度的子列表,并充满了原始列表的最后元素和默认值(例如无)。

这样,如果我这样做:

pending_tasks = stats.pending[44:46]

,待处理列表仅包含30个元素,它应返回两个默认元素的列表,例如:[none,none];而不是空列表([]),这是列表的默认行为。

我想我已经知道如何在普通方法/函数中进行操作,但是我想以最干净的方式进行操作,并尝试遵循@property方法。

非常感谢!

这并不容易,因为切片操作是您要修改的操作,并且在property返回原始列表之后发生了。不过,这并非不可能,您只需要用另一个对象包装常规列表,以照顾您的切片。将会变得多么容易或困难取决于您需要包装器要实现的列表接口中的多少。如果您只需要索引和切片,这真的很容易:

class PadSlice(object):
    def __init__(self, lst, default_value=None):
        self.lst = lst
        self.default_value
    def __getitem__(self, index):
        item = getitem(self.lst, index)
        if isinstance(index, slice):
            expected_length = (index.stop - index.start) // (index.step or 1)
            if len(item) != expected_length:
                item.extend([default_value] * (expected_length - len(item)))
        return item

此代码可能对负步切片或未指定终点之一的切片不正常(它具有检测省略的步骤,因为这很常见,它确实具有逻辑)。如果这对您很重要,您可能可以修复那些角案件。

这并不容易。您返回的对象(列表)如何知道稍后将如何切片?但是,您可以子类list,并且可以覆盖__getitem____getslice__(仅Python2):

class L(list):
    def __getitem__(self, key):
        if isinstance(key, slice):
            return [list(self)[i] if 0 <= i < len(self) else None for i in xrange(key.start, key.stop, key.step or 1)]
        return list(self)[key]
    def __getslice__(self, i, j):
        return self.__getitem__(slice(i, j))

这将用None粘贴所有切片,与负索引和步骤!= 1完全兼容。在您的属性中,返回实际列表的L版本:

@property
def pending(self):
    return L(self._pending)

您可以构造一个新类,该类是list的子类。然后,您可以将__getitem__魔术方法超载以将[]运算符重载到适当的行为。考虑list的这个子类称为MyList

class MyList(list):
    def __getitem__(self, index):
        """Modify index [] operator"""
        result = super(MyList, self).__getitem__(index)
        if isinstance(index, slice):
            # Get sublist length.
            if index.step: # Check for zero to avoid divide by zero error
                sublist_len = (index.stop - index.start) // index.step
            else:
                sublist_len = (index.stop - index.start)
            # If sublist length is greater (or list is shorter), then extend
            # the list to length requested with default value of None
            if sublist_len > len(self) or index.start > len(self):
                result.extend([None for _ in range(sublist_len - len(result))])
        return result

然后,您只需更改pending方法以返回MyList类型而不是list

class Stats(object):
    @property
    def pending(self):
        return MyList(self._pending)

希望这会有所帮助。

最新更新