[Tutor] Iterators, example (need help)

Prasad, Ramit ramit.prasad at jpmorgan.com
Tue Oct 23 01:32:41 CEST 2012


Bryan A. Zimmer wrote:
> Hello, all.
> 
> 
> I am a long-time programmer with little experience in Python, but am
> trying to learn. The example program, attached, is a toy app with a
> GUI that merely prints out environment keys and their associated
> values.
> 
> I know there are errors in the program, but I wanted to see if someone
> can tell me something about the iterator "magic" that this program is
> trying to use.
> 
> The whole program was designed on, and written for cygwin (bash shell)
> under Windows 7. The python version is 2.6.3. I haven't tried it on
> other platforms but it "should" work under Windows and Linux.
> 
> I want to understand what I stumbled into here in order to develop the
> code and use similar techniques in the future. Specifically, is it
> really necessary to use __iter__ and __next__ as system functions
> (with the double underscores)? I can program the basic program in any
> number of ways but was enthralled by the concept of iterators and
> generators.

Typically you call iter(obj)  and next(iter_object) when you need to
call them and not .__iter__() or .__next__(). This applies to many
built-in commands like len(obj).

> 
> I know I missed the mark while trying to use the pythonic idiom, but maybe
> you can help me see where I went astray.
> 
> Hopefully you will forgive my home-grown oop and GUI skills, I am from
> the old school of programming, prior to when these became necessities.

Practice (and constructive criticiscm) makes perfect. Also note,
I am unfamiliar with Tkinter and am basing some comments off experience
with wxpython. Some things will be different but principles should
be fairly similar.

> 
> The original inspiration for the code came from "Dive Into Python",
> from the section about iterators and classes, from the fibonacci2.py
> example.
> 
> Thank you
> 
> Bryan A. Zimmer
> --------------------------------------------------------------------
> 
> from Tkinter import *

This is a frowned upon import style as it can easily override existing
names. Instead use:

import Tkinter as tk # You can change "tk" to something else.
# And then access everything in Tkinter as...
self.fm1 = tk.Frame() # use tk or whatever you imported Tkinter "as".

> import os
> 
> ignore1='''
> 
> This program works to do what I set out to do as a first step
> BAZ 10/19/2012
> 
> '''
> 
> 
> class Env:
>     ''' This class represents the environment of the current process'''
>     def __init__(self, master=None):
>         self.d = os.environ.keys()
>         self.v = os.environ.values()
>         self.i = 0
>         self.y = self.v[self.i]
>         self.x = self.d[self.i]

Why are you storing these values? They will not update
in "realtime". Of course, environment variables may not
change so this might not matter.

> 
> 
>     def __iter__(self):
>         self.x = self.d[self.i]
>         self.y = self.v[self.i]
>         return self

Environment variables do not seem like something that really
needs to be iterable. Although, if I was going to do that
I would instead just store os.environ dictionary (instead
of keys and values) and iterate over that.

Also, if you are going to return self, you need to change
`def __next__` to `def next`. At least, that is true for
Python 2, but it may not be true in Python 3.

>>> e = Env()
>>> ie = iter(e)
>>> next(ie)
TypeError: instance has no next() method
>>> for o in ie:
...     print o
...     
TypeError: instance has no next() method

>>> for o in e:
...     print o
...     
TypeError: instance has no next() method

> 
> 
> 
>     def __next__(self,master=None):
>         global s1
>         global s2
> 
>         self.i += 1

This incrementing of i should probably done at the end of the 
loop, because otherwise you always lose what is stored in self.d[0] /self.v[0]. Or you can set self.i to -1 initially.

>         if (self.i < len(self.d)):
>             self.x = self.d[self.i]
>             self.y = self.v[self.i]
> 
>         if (self.x):
>             print self.x, '==>', self.y
>             s1.set(self.x)
>             s2.set(self.y)

What are s1 and s2? Why should they be global? Why not return 
here directly? What happens if the key is None?

>         else:
>             print ("raising Stop Iteration")
>             raise StopIteration
>         return ((self.x, self.y))
> 

It seems like you are replicating the functionality for dict.items().
I suppose this is mostly a learning exercise so that is fine, otherwise 
I would just use a dictionary instead.

> 
> class App(object):
>     ''' This is the main GUI app'''
>     def __init__(self,master):
>         self.createWidgets()
> 
>     def createWidgets(self,master=None):
>         global s1
>         global s2
>         self.fm1 = Frame(master)
>         self.fm2 = Frame(master)
>         self.fm1.pack(side = TOP, anchor=NW, fill=BOTH,expand=YES)
>         self.fm2.pack(side = TOP, anchor=SW, fill=BOTH,expand=YES)

s1 and s2 should be passed in and not be a global variable.
If you need to update the data later, then create an update
method instead of looking at global variables. 

> 
>         s1=StringVar()
>         s1.set(env.x)

env is not set in the code and so I have no idea what this refers to.
I suppose by the time this gets created there is an env in the global
scope, but I think this is chancy and will only work from this script.
You cannot reuse or import class App from another program.
Instead pass env into the __init__ method.

>         self.l1=Label(self.fm1,text='Var:')
>         self.l1.pack(side=LEFT,fill=X,expand=NO,padx=10)
>         e1=Entry(self.fm1,textvariable=s1,width=40)
>         e1.pack(side=LEFT,fill=X,expand=YES,padx=10)
> 
>         self.l2=Label(self.fm2,text='Val: ')
>         self.l2.pack(side=LEFT,fill=X,expand=NO,padx=10)
>         s2=StringVar()
>         s2.set(env.y)
> 
>         e2=Entry(self.fm2,textvariable=s2, width=40)
>         e2.pack(side=LEFT,fill=X,expand=YES,padx=9)
> 
>         self.b1=Button(self.fm2,text='Next',command=env.__next__)

I have not run this program, but I am pretty sure this button will
not update e1 and e2. Not to mention that you need to store e1/e2 
somewhere you can access later. I would just add it to self. Otherwise 
you will have no way of modifying the widgets. 

What will happen is env.__next__() will return some value, but it 
will return it to the default event handler where nothing will happen. 
You need to create a method that will call env.__next__ and do 
something  like the following (this is untested; while the theory 
should be sound, putting the code together might need some more 
changes than listed here).

    self.b1=Button(self.fm2,text='Next',command=self.handle_button)

def handle_button( self, event ):
    x,y = self.env.__next__()
    self.e1['text'] = x # Not sure how you set the value in Tkinter.
    self.e2['text'] = y

>         self.b1.pack(side=TOP,anchor=S,fill=X,expand=YES,padx=10)
> 
> 
> if __name__=='__main__':
>     env=Env()
>     root = Tk()
>     root.option_add('*font', ('verdana', 12, 'bold'))
>     root.title("Pack - Example 12")
>     app=App(root)

This should really have env passed into.
      app=App(root, env)


>     root.mainloop()
> ----------------------------------------------------------------------------
> -------
> 


Ramit Prasad


This email is confidential and subject to important disclaimers and
conditions including on offers for the purchase or sale of
securities, accuracy and completeness of information, viruses,
confidentiality, legal privilege, and legal entity disclaimers,
available at http://www.jpmorgan.com/pages/disclosures/email.  


More information about the Tutor mailing list