Python,懒惰列表

时间:2010-03-08 03:52:09

标签: python lazy-evaluation

是否有可能在Python中懒惰地评估列表?

例如

a = 1
list = [a]
print list
#[1]
a = 2
print list
#[1]

如果列表设置为懒惰评估,那么最后一行将是[2]

4 个答案:

答案 0 :(得分:10)

“懒惰”评估的概念通常带有函数式语言 - 但是在那些你无法将两个不同的值重新分配给相同的标识符的情况下,所以甚至不能复制你的例子。

重点不在于懒惰 - 保证使用标识符与获取对标识符引用的相同值的引用相同,并且重新分配标识符,裸名,保证使标识引用与它们不同的值。对第一个值(对象)的引用不会丢失。

考虑一个类似的例子,其中重新分配到裸名称不起作用,而是任何其他类型的变异(对于可变对象,当然 - 数字和字符串是不可变的),包括对某事物的赋值<强>其他而不是一个简单的名字:

>>> a = [1]
>>> list = [a]
>>> print list
[[1]]
>>> a[:] = [2]
>>> print list
[[2]]

由于没有a - ...重新分配明确名称a,而是a[:] = ...重新分配a内容,因此很容易让Python像你想的那样“懒惰”(事实上它需要一些努力才能使它“渴望”! - )...如果懒惰与渴望与这些情况中的任何一个有任何关系(它没有'吨;。 - )

请注意“分配给一个裸名”的完全简单的语义(vs分配给其他任何东西,可以通过适当地使用你自己的类型进行各种调整和控制),以及“lazy vs eager”的视错觉“可能有希望消失; - )

答案 1 :(得分:6)

Python是not really very lazy in general

您可以使用生成器来模拟延迟数据结构(如无限列表等),但就使用普通列表语法等等而言,您不会有懒惰。

答案 2 :(得分:5)

在寻找真正的懒惰列表实现时遇到过这篇文章,但尝试解决这个问题听起来很有趣。

以下实现基本上是基本上要求的:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent"
    android:orientation="vertical">


    <LinearLayout
        android:layout_marginTop="20dp"
        android:background="@android:color/white"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:layout_marginTop="-20dp"
            android:layout_width="wrap_content"
            android:layout_height="50dp"
            android:layout_gravity="center"
            android:src="@drawable/ic_bell"/>

        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:background="@drawable/border"
            android:gravity="top|left"
            android:inputType="textMultiLine"
            android:lines="8"
            android:maxLines="10"
            android:minLines="6"
            android:scrollbars="vertical"/>
    </LinearLayout>

    <LinearLayout
        android:background="@android:color/white"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:weightSum="2">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@color/colorPrimary"
            android:text="Submit"
            android:textColor="@color/white"/>

        <View
            android:layout_width="4dp"
            android:layout_height="match_parent"
            android:background="@android:color/transparent"/>
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@color/colorPrimary"
            android:text="Cancel"
            android:textColor="@color/white"/>

    </LinearLayout>
</LinearLayout>

你这样使用它:

from collections import Sequence

class LazyClosureSequence(Sequence):
    def __init__(self, get_items):
        self._get_items = get_items

    def __getitem__(self, i):
        return self._get_items()[i]

    def __len__(self):
        return len(self._get_items())

    def __repr__(self):
        return repr(self._get_items())

这显然很可怕。

答案 3 :(得分:0)

这是一个只读的惰性列表,它只需要一个预定义的长度和一个缓存更新函数:

import copy
import operations
from collections.abc import Sequence
from functools import partialmethod
from typing import Dict, Union

def _cmp_list(a: list, b: list, op, if_eq: bool, if_long_a: bool) -> bool:
    """utility to implement gt|ge|lt|le class operators"""
    if a is b:
        return if_eq
    for ia, ib in zip(a, b):
        if ia == ib:
            continue
        return op(ia, ib)

    la, lb = len(a), len(b)
    if la == lb:
        return if_eq
    if la > lb:
        return if_long_a
    return not if_long_a


class LazyListView(Sequence):
    def __init__(self, length):
        self._range = range(length)
        self._cache: Dict[int, Value] = {}

    def __len__(self) -> int:
        return len(self._range)

    def __getitem__(self, ix: Union[int, slice]) -> Value:
        length = len(self)

        if isinstance(ix, slice):
            clone = copy.copy(self)
            clone._range = self._range[slice(*ix.indices(length))]  # slicing
            return clone
        else:
            if ix < 0:
                ix += len(self)  # negative indices count from the end
            if not (0 <= ix < length):
                raise IndexError(f"list index {ix} out of range [0, {length})")
            if ix not in self._cache:
                ...  # update cache
            return self._cache[ix]

    def __iter__(self) -> dict:
        for i, _row_ix in enumerate(self._range):
            yield self[i]

    __eq__ = _eq_list
    __gt__ = partialmethod(_cmp_list, op=operator.gt, if_eq=False, if_long_a=True)
    __ge__ = partialmethod(_cmp_list, op=operator.ge, if_eq=True, if_long_a=True)
    __le__ = partialmethod(_cmp_list, op=operator.le, if_eq=True, if_long_a=False)
    __lt__ = partialmethod(_cmp_list, op=operator.lt, if_eq=False, if_long_a=False)

    def __add__(self, other):
        """BREAKS laziness and returns a plain-list"""
        return list(self) + other

    def __mul__(self, factor):
        """BREAKS laziness and returns a plain-list"""
        return list(self) * factor

    __radd__ = __add__
    __rmul__ = __mul__


注意这个类也在this SO中讨论。