浅析java和python的迭代

先说两句废话

作为一个算法工程师,一般都要同时兼顾着模型预研和工程部署实现的工作。

如果说现在模型预研用得最多的是python(简单,易上手,除了慢和写得好需要极强的内容外,几乎没有别的缺点)这个目前应该是没有争议的,但是在工程部署上就不大统一了,有的公司用java,有的用c++,还有的用JavaScript(没想到吧,毕竟js是可以贯通前端+后端的神器,还可以直接用TensorFlow),当然,他们几个之间的好处坏处我这里也懒得或者说不想叙述了,只能说,本渣更注重后台实现,所以工程上几乎都是java。

 

重点是,如果真要做一个工程师,日常应该是至少要熟练运用2门程序语言以上的(本渣就是2门)。但是混着用两门语言,而且几乎使用频率一样有一个非常尴尬的特点——非常容易混淆,例如我写着java可能会突然来了一句for i in range(n_len),写着python可能突然来一句if(true){};

当然,这点其实这是一点小插曲,毕竟idea或者pycharm一报错,瞬间可以反应过来。

重点是,不同语言之间的一些重点核心的概念,也容易混,例如迭代、例如正则、例如多线程。所以久着久着都得无聊翻书看看,后面翻书多了,一发狠,还是将比较重要的那几个的概念在java和python区分开,毕竟点击自己的blog比翻书还是轻松多了。

这次先讲python和java的迭代概念上的区别和联系。

 

什么是迭代?

 

⽤简单的话讲,它就是从某个地⽅(⽐如⼀个列表)取出⼀个元素的过程。当我们使⽤⼀个循环来遍历某个东西时,这就叫⼀个迭代[1]。

更简单的话,只用能用for语句(java和python都有)进行循环的,就是迭代。

这就是java和python关于迭代的所有共同之处

 

除此之外,java和python关于迭代的概念还是相差有点大的,需要重点掌握下面几个关键词:

java:Iterable,Iterator

python:Iterable, Iterator, Generator

 

先谈在迭代上更容易实现的java

 

Java的迭代

java的迭代有且只跟两个接口有关,Iterable,Iterator。而且这两个接口是强关联在一起,缺一不可。

 

Iterable: 作用是选择Iterator,可用if-else条件判断的方法选择不同的iterator

public interface Iterable<T> {
    /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    Iterator<T> iterator();
}

Iterator:具体的迭代循环方式

public interface Iterator<E> {
    /**
     * Returns {@code true} if the iteration has more elements.
     * (In other words, returns {@code true} if {@link #next} would
     * return an element rather than throwing an exception.)
     *
     * @return {@code true} if the iteration has more elements
     */
    boolean hasNext();

    /**
     * Returns the next element in the iteration.
     *
     * @return the next element in the iteration
     * @throws NoSuchElementException if the iteration has no more elements
     */
    E next();

    /**
     * Removes from the underlying collection the last element returned
     * by this iterator (optional operation).  This method can be called
     * only once per call to {@link #next}.  The behavior of an iterator
     * is unspecified if the underlying collection is modified while the
     * iteration is in progress in any way other than by calling this
     * method.
     *
     * @implSpec
     * The default implementation throws an instance of
     * {@link UnsupportedOperationException} and performs no other action.
     *
     * @throws UnsupportedOperationException if the {@code remove}
     *         operation is not supported by this iterator
     *
     * @throws IllegalStateException if the {@code next} method has not
     *         yet been called, or the {@code remove} method has already
     *         been called after the last call to the {@code next}
     *         method
     */
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
}

在java中,要实现迭代,必须实现Iterable接口——该接口必须实现的iterator方法就是要返回一个满足Iterator接口的对象!

而Iterator接口也必须依附在Iterable上才能正常的工作,不然它就是个普通的接口。

以算法第四版关于背包的实现为例:

public class Bag<Item> implements Iterable<Item>{
    private Node first;//栈顶
    private int N;

    private class Node{
        Item item;
        Node next;
    }

    public int size(){
        return N;
    }

    public boolean isEmpty(){
        return first==null;
    }

    public void add(Item item){
        Node oldFirst = first;
        first = new Node();
        first.item= item;
        first.next = oldFirst;
        N++;
    }



    public Item pop(){
        N--;
        Item item = first.item;
        first=first.next;
        return item;
    }

    public Iterator<Item> iterator(){
        return new ListIterator();
    }

    private class ListIterator implements Iterator<Item>{
        private Node current = first;

        @Override
        public boolean hasNext() {
            return current==null;
        }

        public Item next(){
            Item item = current.item;
            current = current.next;
            return item;
        }
        public void remove(){}
    }

}

当然,为什么要把Iterable和Iterator进行分开呢,因为在例子中iterator方法其实是可以根据其他的判断条件进行不同迭代器的选择,根据条件的不同,我可以像背包那样无序输出,也可以根据栈那样进行先进后出,或者队列那样先进先出。

 

Python的迭代

python的迭代其实比java的复杂一丢丢。

虽然python中也有Iterable和Iterator的关联,但是!它们只是有可能关联,不是java的那种强关联。如果说python中迭代概念存在强关联的,那是Generator一定是一个Iterator!

先说各自的概念:

可迭代对象(Iterable):⼀个可迭代对象是Python中任意的对象,只要它定义了可以返回⼀个迭代器的__iter__ ⽅法,或者定义了可以⽀持下标索引的__getitem__⽅法,可以用for进行遍历。

迭代器(Iterator):⼀个迭代器是任意⼀个对象,只要它定义了⼀个__next__⽅法。迭代器本身表示一个惰性计算的序列:数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据。

生成器(Genertor):自定义最常用的迭代器。可以用更加优雅的方式实现迭代器的效果——不用专门写__next__方法:1. 使用yield关键字。2.要把一个列表生成式的[]改成()

 

为什么说Iterable和Iterator不是强关联?——因为我们可以用__getitem__方法进行遍历,最常见的在pytorch的自定义dataset的时候就是这样。

用一个更简单的例子来验证:

class Library(object):
    def __init__(self):
        self.books = [1, 2, 3]
        self.index = -1

    def __getitem__(self, i):
        return str(self.books[i])+"test"
    
l = Library()
print(l[1])
for i in l:
    print(i)

 

而实际在进行模型训练时候,生成器是非常常用的!(准确说yield关键字)。因为生成器是迭代器的一种,不需要我们把数据加入到内存之中,例如gensim训练word2vec的数据集的过程中:

class MySentences(object):
    def __init__(self, dirname):
        self.dirname = dirname

    def __iter__(self):
        for fname in os.listdir(self.dirname):
            for line in open(os.path.join(self.dirname, fname)):
                yield line.split()

sentences = MySentences('/some/directory') # a memory-friendly iterator
model = gensim.models.Word2Vec(sentences)

这里用__iter__方法实现了一个生成器(迭代器),以实现了整个读取数据的循环——用yield方法避免了像java那样要使用另一个类,并且实现__next__方法,其实到目前为止,本渣几乎都是用生成器来替代迭代器(其实主要是我写得代码还不够多)。

 

 

参考链接:

[1] https://www.liaoxuefeng.com/wiki/1016959663602400/1017316949097888

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值