将可调用对象隐式绑定到实例

时间:2012-06-14 15:51:29

标签: python class iterator callable

我有这段代码:

class LFSeq: # lazy infinite sequence with new elements from func
    def __init__(self, func):
        self.evaluated = []
        self.func = func
    class __iter__:
        def __init__(self, seq):
            self.index = 0
            self.seq = seq
        def next(self):
            if self.index >= len(self.seq.evaluated):
                self.seq.evaluated += [self.seq.func()]
            self.index += 1
            return self.seq.evaluated[self.index - 1]

我明确地希望LFSeq.__iter__LFSeq的实例一样受限于任何其他用户定义的函数。

它不能以这种方式工作,因为只有用户定义的函数是有界的而不是类。

当我介绍像

这样的函数装饰器时
def bound(f):
    def dummy(*args, **kwargs):
        return f(*args, **kwargs)
    return dummy

然后我可以用它来装饰__iter__并且它可以工作:

...
@bound
class __iter__:
    ...

然而,这感觉有点hacky和不一致。还有其他方法吗?应该是这样吗?

我想是的,因为否则LFSeq.__iter__LFSeq(None).__iter__将不再是同一个对象(即类对象)。也许关于有界函数的整个事情应该是语法糖而不是在运行时。但是,另一方面,语法糖不应该真正依赖于内容。我想在某个地方必须进行一些权衡。

2 个答案:

答案 0 :(得分:3)

您尝试做的最简单的解决方案是将__iter__()方法定义为生成器函数:

class LFSeq(object):
    def __init__(self, func):
        self.evaluated = []
        self.func = func
    def __iter__(self):
        index = 0
        while True:
            if index == len(self.evaluated):
                self.evaluated.append(self.func())
            yield self.evaluated[index]
            index += 1

你的方法必须处理Python对象模型的许多细微之处,并且没有理由去那条路。

答案 1 :(得分:2)

在我看来,最好的解决方案是@Sven one,毫无疑问。那就是说,你想要做的事情看起来非常黑暗 - 我的意思是,将__iter__定义为一个类。它不起作用,因为在另一个类中声明一个类不像定义一个方法,而是它就像定义一个属性。代码

class LFSeq:
    class __iter__:

大致相当于将创建一个类字段的属性:

class LFSeq:
     __iter__ = type('__iter__', (), ...)

然后,每次在类中定义属性时,都会绑定到类本身,而不是特定实例。

我认为你应该遵循@Sven解决方案,但是如果你真的想要出于任何其他原因定义一个类,那么你似乎很幸运,因为你的生成器类并不依赖于LFSeq实例本身的任何内容。只需在外面定义迭代器类:

class Iterator(object):
    def __init__(self, seq):
        self.index = 0
        self.seq = seq
    def next(self):
        if self.index >= len(self.seq.evaluated):
            self.seq.evaluated += [self.seq.func()]
        self.index += 1
        return self.seq.evaluated[self.index - 1]

并在LFSeq.__iter__()方法中实例化它:

class LFSeq(object): # lazy infinite sequence with new elements from func
    def __init__(self, func):
        self.evaluated = []
        self.func = func
    def __iter__(self):
        return Iterator(self)

如果最终需要将迭代器类绑定到实例,则可以在LFSeq.__init__()中定义迭代器类,将其放在self属性上并在LFSeq.__iter__()中实例化它: / p>

class LFSeq(object): # lazy infinite sequence with new elements from func
    def __init__(self, func):

        lfseq_self = self # For using inside the iterator class

        class Iterator(object): # Iterator class defined inside __init__
            def __init__(self):
                self.index = 0
                self.seq = lfseq_self # using the outside self
            def next(self):
                if self.index >= len(self.seq.evaluated):
                    self.seq.evaluated += [self.seq.func()]
                self.index += 1
                return self.seq.evaluated[self.index - 1]

        self.iterator_class = Iterator # setting the itrator
        self.evaluated = []
        self.func = func

    def __iter__(self):
        return self.iterator_class() # Creating an iterator
然而,正如我所说,@Sven解决方案看起来更精细。我刚刚回答了尝试解释为什么你的代码没有按照你的预期行事,并提供一些关于做你想做的事情的信息 - 尽管如此,这可能是有用的。