Critique of first python code

Z

Zack

Hi all. I'm just starting to pick up python. I wanted to play with nested
lists so first I wrote a little bit of code to create arbitrarily nested
lists (grow). Then I wrote a breadth first search. I'm putting this small
snippet up asking for criticism. Was there a more elegant way to do what I'm
doing? Anything that goes against convention? Anything that fingers me as a
c++ programmer? Are there standard modules that do these kind of things?
Thanks for any feedback.

##############################
from random import randint

Val = 0

def grow(L,depth):
'''grows L by appending integers and arbitrarily nested lists with a
maximum
depth. Returns L.'''
global Val
if depth == 0:
return L
else:
choice = randint(1,2)
if 1 == choice:
# add a numerical literal
Val += 1
L.append(Val)
return grow(L,depth-1)
elif 2 == choice:
# add a list
L.append( grow([],depth-1) )
return grow(L,depth-1)

def traverse(L, count=0):
'''Prints the non iterable object of L in breadth first order.
Returns count + the number of non iterables in L.'''
if L == []:
return count
n = []
for e in L:
if not hasattr(e,'__iter__'):
print e,
count += 1
else:
n[:] += e
print '\n'
return traverse(n,count)

L = grow([],10)
C = traverse(L)
##############################
 
A

Arnaud Delobelle

Hi all. I'm just starting to pick up python. I wanted to play with nested
lists so first I wrote a little bit of code to create arbitrarily nested
lists (grow). Then I wrote a breadth first search.  I'm putting this small
snippet up asking for criticism. Was there a more elegant way to do what I'm
doing? Anything that goes against convention?  Anything that fingers me as a
c++ programmer? Are there standard modules that do these kind of things?
Thanks for any feedback.

##############################
from random import randint

Val = 0

def grow(L,depth):
   '''grows L by appending integers and arbitrarily nested lists with a
maximum
   depth. Returns L.'''
   global Val
   if depth == 0:
      return L
   else:
      choice = randint(1,2)
      if 1 == choice:
         # add a numerical literal
         Val += 1
         L.append(Val)
         return grow(L,depth-1)
      elif 2 == choice:
         # add a list
         L.append( grow([],depth-1) )
         return grow(L,depth-1)

How about:

from itertools import count
from random import randrange

def grow(L, depth, counter=count()):
'''grow L by appending integers and arbitrarily nested lists with a
maximum depth. Returns L.'''
if depth == 0:
return L
else:
L.append(counter.next() if randrange(2) else grow([], depth-1))
return grow(L, depth-1)

def traverse(L, count=0):
   '''Prints the non iterable object of L in breadth first order.
   Returns count + the number of non iterables in L.'''
   if L == []:
      return count
   n = []
   for e in L:
      if not hasattr(e,'__iter__'):
         print e,
         count += 1
      else:
         n[:] += e
I would write n.extend(e) here (or n += e)
   print '\n'
   return traverse(n,count)

L = grow([],10)
C = traverse(L)
##############################
 
T

Tomek Paczkowski

Zack said:
Hi all. I'm just starting to pick up python. I wanted to play with nested
lists so first I wrote a little bit of code to create arbitrarily nested
lists (grow). Then I wrote a breadth first search. I'm putting this small
snippet up asking for criticism. Was there a more elegant way to do what
...

You can try to put your code through pylint. It will give you some automatic
critique.
 
G

George Sakkis

Hi all. I'm just starting to pick up python. I wanted to play with nested
lists so first I wrote a little bit of code to create arbitrarily nested
lists (grow). Then I wrote a breadth first search. I'm putting this small
snippet up asking for criticism. Was there a more elegant way to do what I'm
doing? Anything that goes against convention? Anything that fingers me as a
c++ programmer? Are there standard modules that do these kind of things?
Thanks for any feedback.
##############################
from random import randint
def grow(L,depth):
'''grows L by appending integers and arbitrarily nested lists with a
maximum
depth. Returns L.'''
global Val
if depth == 0:
return L
else:
choice = randint(1,2)
if 1 == choice:
# add a numerical literal
Val += 1
L.append(Val)
return grow(L,depth-1)
elif 2 == choice:
# add a list
L.append( grow([],depth-1) )
return grow(L,depth-1)

How about:

from itertools import count
from random import randrange

def grow(L, depth, counter=count()):
'''grow L by appending integers and arbitrarily nested lists with a
maximum depth. Returns L.'''
if depth == 0:
return L
else:
L.append(counter.next() if randrange(2) else grow([], depth-1))
return grow(L, depth-1)

Or you may write a more flexible generator version of the above. If
you're not familiar with generators, think of them as lazy sequences
that generate successive elements only when needed:

import random
from itertools import count

def igrow(depth, next=count(1).next, nest=list, random=random.random):
'''Generate integers and arbitrarily nested iterables with a
maximum depth.'''
if depth:
depth -= 1
yield next() if random()<0.5 else
nest(igrow(depth,next,nest,random))
for e in igrow(depth,next,nest,random):
yield e


With this you can just as easily generate nested tuples (or other
containers) instead of lists:

nested = tuple(igrow(10, nest=tuple))

You may even avoid allocating nested containers altogether:

from types import GeneratorType

for x in igrow(10, nest=iter):
if isinstance(x, GeneratorType):
# process nested generator
else:
# x is an 'atom'


HTH,
George
 
Z

Zack

How about:
from itertools import count
from random import randrange
def grow(L, depth, counter=count()):
'''grow L by appending integers and arbitrarily nested lists with a
maximum depth. Returns L.'''
if depth == 0:
return L
else:
L.append(counter.next() if randrange(2) else grow([], depth-1))
return grow(L, depth-1)

Or you may write a more flexible generator version of the above. If
you're not familiar with generators, think of them as lazy sequences
that generate successive elements only when needed:

import random
from itertools import count

def igrow(depth, next=count(1).next, nest=list, random=random.random):
'''Generate integers and arbitrarily nested iterables with a
maximum depth.'''
if depth:
depth -= 1
yield next() if random()<0.5 else
nest(igrow(depth,next,nest,random))
for e in igrow(depth,next,nest,random):
yield e

With this you can just as easily generate nested tuples (or other
containers) instead of lists:

nested = tuple(igrow(10, nest=tuple))

You may even avoid allocating nested containers altogether:

from types import GeneratorType

for x in igrow(10, nest=iter):
if isinstance(x, GeneratorType):
# process nested generator
else:
# x is an 'atom'

HTH,
George

The generators you show here are interesting, and it prodded me on how
to add tuples but at the moment (I'm a python newbie) the generator
seems less readable to me than the alternative. After some input from
Scott David Daniels I changed some of the unnecessary recursion to
while loops. I completely over thought my problem. I've fetched pylint
and I'll be sure to keep it in my toolkit. What does everyone think of
the code below? Are generator functions a more pythonic (preferred?)
way of doing things or will my while loops escape mocking? Thanks for
the feedback this has been a great exercise for me.

import random
from itertools import count

_Val = count(1)

def grow(seq, depth):
'''
Grows seq with arbitrarily appended integers, lists and tuples.

At least depth elements will be added to seq and seq will not grow
more than depth levels deep.
Returns seq.

'''
while depth > 0:
choice = random.random()
if choice < .5:
seq.append(_Val.next())
elif choice < .75:
seq.append(list(grow([], depth-1)))
else:
seq.append(tuple(grow([], depth-1)))
depth -= 1
return seq


def traverse(seq):
'''
Breadth first traversal of seq.

Prints the non iterable objects of seq in breadth first order.
Returns the number of atoms (non iterables) in seq.

'''
counter = 0
while seq:
below = []
for item in seq:
if hasattr(item, '__iter__'):
below.extend(item)
else:
print item,
counter += 1
#intentional blank line to distinguish long lines that wrap
print '\n'
seq = below
return counter

L = grow([],10)
C = traverse(L)
 
T

TerryP

Tomek said:
You can try to put your code through pylint. It will give you some
automatic critique.

There is a pylint !?

That one is defiantly making my workstation later just for fun hehe.
 
D

Dan Bishop

[snip]

The generators you show here are interesting, and it prodded me on how
to add tuples but at the moment (I'm a python newbie) the generator
seems less readable to me than the alternative. After some input from
Scott David Daniels I changed some of the unnecessary recursion to
while loops. I completely over thought my problem. I've fetched pylint
and I'll be sure to keep it in my toolkit. What does everyone think of
the code below? Are generator functions a more pythonic (preferred?)
way of doing things or will my while loops escape mocking? Thanks for
the feedback this has been a great exercise for me.

[code example snipped]

Your functions look like good candidates for generators, but I
wouldn't mock you for using while loops.

I will say, however, that hasattr(item, '__iter__') isn't a perfect
way of checking whether an object is iterable: Objects that just
define __getitem__ are iterable too (e.g., UserList).
 
M

Matthew Marshall

Dan said:
I will say, however, that hasattr(item, '__iter__') isn't a perfect
way of checking whether an object is iterable: Objects that just
define __getitem__ are iterable too (e.g., UserList).

Speaking of which, what *is* the best way to check if an object is
iterable?

I always wrap iter(item) in a try/except block, but is there an
isiterable() function somewhere?

MWM
 
G

George Sakkis

Speaking of which, what *is* the best way to check if an object is
iterable?

I always wrap iter(item) in a try/except block,

Yes, that's the most foolproof way.
but is there an
isiterable() function somewhere?

Not AFAIK, but if I had to write one, I would use

isiterable = lambda x: hasattr(x, '__iter__') or
hasattr(x, '__getitem__')


In Python 3 it will be isinstance(x, Iterable).

George
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top