python自编循环语句_如何优雅地自定义类实现Python的for循环 —— 以链表为例

Python的列表、元组、集合、字典都支持使用for循环,Python的for循环也使得遍历一个可迭代对象的每个元素十分容易,那么如何自定义一个类来使它实现Python的for循环呢?

为了更好地说明这个方法,我用单链表来举例:

class Node(object):

def __init__(self, item, next_=None):

self.item = item

self.next = next_

class SLList(object):

def __init__(self, item):

self.__sentinel = Node(0)

self.__sentinel.next = Node(item)

self.__size = 1

# 属性绑定,使得self.size无法被用户赋值

@property

def size(self):

return self.__size

def addFirst(self, item):

self.__sentinel.next = Node(item, self.__sentinel.next)

self.__size += 1

def addLast(self, item):

p = self.__sentinel

while P.next is not None:

p = p.next

p.next = Node(item)

self.__size += 1

其中,Node类是单链表的节点类,SLList是一个带sentinel的单链表,我这里只简单实现了一些addFirst和addLast方法。

首先我们回顾一下Java是如何实现增强的for循环的(即for each),类似的代码如下:

/* Singly linked list with generics*/

public class SLList implements Iterable {

private Node sentinel;

private int size;

private class Node{

T item;

Node next;

public Node(T item, Node next){

this.item = item;

this.next = next;

}

}

public SLList(T item){

size = 1;

sentinel.next = new Node(item, null);

}

public void addFirst(T item){

sentinel.next = new Node(item, sentinel.next);

size++;

}

public void addLast(T item){

Node p = this.sentinel;

while (p.next != null){

p = p.next;

}

p.next = new Node(item, null);

}

private class SLListIterator implements Iterator{

private Node pointer = sentinel;

@Override

public boolean hasNext() {

return pointer.next != null;

}

@Override

public T next() {

pointer = pointer.next;

return pointer.item;

}

}

@Override

public Iterator iterator() {

return new SLListIterator();

}

}

在Java中,我们使SLList类实现了iterable接口返回一个Iterator对象,在for each循环中,我们其实是先调用SLList.iterator()方法得到一个Iterator对象,再调用Iterator对象的next方法直到hasNext方法返回false.

接下来我们回到python上来,Python实现for循环的主要方法有两个,分别是 __iter__ 和 __next__方法,分别对应 iter 和 next两个内置函数,以下是Python for循环的一个慢动作:

在for i in range(3)语句中,首先由内置函数iter生成一个iterable object,再不断地调用这个对象的next的方法并赋值给 i 这个变量,直到这个对象的next方法报出“StopIteration”这个错误的时候停止迭代。

for i in range(3):

print(i) # 依次打印

it = iter([0,1,2])

print(next(it))

print(next(it))

print(next(it))

print(next(it)) # Traceback: StopIteration

好,那么我们该如何让我们自定义的单链表也能像普通的列表,元组一样支持for循环呢?

首先,我们写入__iter__ 方法,这个方法返回一个可迭代的对象,这个对象支持next方法,并在“没有下一个”的时候 raise StopIteration 错误(就像Java的 .iterator()方法返回一个带有.next()和.hasNext()方法的实现了Iterable接口的对象一样)。那么我们该如何定义__iter__方法返回的对象呢?这里我给出两种思路:

第一种思路:把链表本身(self)返回,再写入链表的__next__方法,示例代码如下:

def __iter__(self):

self.__pointer = self.__sentinel

return self

def __next__(self):

while self.__pointer.next is not None:

self.__pointer = self.__pointer.next

return self.__pointer.item

else:

raise StopIteration()

在每次迭代的一开始(__iter__方法的第一行),我们让指针pointer指向sentinel,并把self返回,当指针pointer非空时,我们将它作为__next__方法的返回值,当pointer为空时,我们raise StopIteration()

第二种思路:定义一个遍历链表所有节点的生成器,我们就可以使用这个生成器自带的next()方法了,示例代码:

def __travel(self):

p = self.__sentinel.next

while p is not None:

yield p.item

p = p.next

def __iter__(self):

return self.__travel

这里我们给iter方法返回了一个迭代器函数,迭代器本身就支持__next__方法. So, it works !!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值