Python NoneType error in some executions of a program
Question: I was testing a code found in a book about Genetic Algorithms and I came up
with an strange mistake. The code is the following:

    
    
    import time
    import random
    import math
    
    people = [('Seymour','BOS'),
              ('Franny','DAL'),
              ('Zooey','CAK'),
              ('Walt','MIA'),
              ('Buddy','ORD'),
              ('Les','OMA')]
    # Laguardia
    destination='LGA'
    
    flights={}
    # 
    for line in file('schedule.txt'):
      origin,dest,depart,arrive,price=line.strip().split(',')
      flights.setdefault((origin,dest),[])
    
      # Add details to the list of possible flights
      flights[(origin,dest)].append((depart,arrive,int(price)))
    
    def getminutes(t):
      x=time.strptime(t,'%H:%M')
      return x[3]*60+x[4]
    
    def printschedule(r):
      for d in range(len(r)/2):
        name=people[d][0]
        origin=people[d][1]
        out=flights[(origin,destination)][int(r[d])]
        ret=flights[(destination,origin)][int(r[d+1])]
        print '%10s%10s %5s-%5s $%3s %5s-%5s $%3s' % (name,origin,
                                                      out[0],out[1],out[2],
                                                      ret[0],ret[1],ret[2])
    
    def schedulecost(sol):
      totalprice=0
      latestarrival=0
      earliestdep=24*60
    
      for d in range(len(sol)/2):
        # Get the inbound and outbound flights
        origin=people[d][1]
        outbound=flights[(origin,destination)][int(sol[d])]
        returnf=flights[(destination,origin)][int(sol[d+1])]
    
        # Total price is the price of all outbound and return flights
        totalprice+=outbound[2]
        totalprice+=returnf[2]
    
        # Track the latest arrival and earliest departure
        if latestarrival<getminutes(outbound[1]): latestarrival=getminutes(outbound[1])
        if earliestdep>getminutes(returnf[0]): earliestdep=getminutes(returnf[0])
    
      # Every person must wait at the airport until the latest person arrives.
      # They also must arrive at the same time and wait for their flights.
      totalwait=0  
      for d in range(len(sol)/2):
        origin=people[d][1]
        outbound=flights[(origin,destination)][int(sol[d])]
        returnf=flights[(destination,origin)][int(sol[d+1])]
        totalwait+=latestarrival-getminutes(outbound[1])
        totalwait+=getminutes(returnf[0])-earliestdep  
    
      # Does this solution require an extra day of car rental? That'll be $50!
      if latestarrival>earliestdep: totalprice+=50
    
      return totalprice+totalwait
    
    
    def geneticoptimize(domain,costf,popsize=50,step=1,
                        mutprob=0.2,elite=0.2,maxiter=100):
      # Mutation Operation
      def mutate(vec):
        i=random.randint(0,len(domain)-1)
        if random.random()<0.5 and vec[i]>domain[i][0]:
          return vec[0:i]+[vec[i]-step]+vec[i+1:] 
        elif vec[i]<domain[i][1]:
          return vec[0:i]+[vec[i]+step]+vec[i+1:]
    
      # Crossover Operation
      def crossover(r1,r2):
        i=random.randint(1,len(domain)-2)
        return r1[0:i]+r2[i:]
    
      # Build the initial population
      pop=[]
      for i in range(popsize):
        vec=[random.randint(domain[i][0],domain[i][1]) 
             for i in range(len(domain))]
        pop.append(vec)
    
      # How many winners from each generation?
      topelite=int(elite*popsize)
    
      # Main loop 
      for i in range(maxiter):
        scores=[(costf(v),v) for v in pop]
        scores.sort()
        ranked=[v for (s,v) in scores]
    
        # Start with the pure winners
        pop=ranked[0:topelite]
    
        # Add mutated and bred forms of the winners
        while len(pop)<popsize:
          if random.random()<mutprob:
    
            # Mutation
            c=random.randint(0,topelite)
            pop.append(mutate(ranked[c]))
          else:
    
            # Crossover
            c1=random.randint(0,topelite)
            c2=random.randint(0,topelite)
            pop.append(crossover(ranked[c1],ranked[c2]))
    
        # Print current best score
        print scores[0][0]
    
      return scores[0][1]
    

This code uses a .txt file called schedule.txt and that it can be downloaded
from <http://kiwitobes.com/optimize/schedule.txt>

When I run the code I put the following, according to the book:

    
    
    >>> domain=[(0,8)]*(len(optimization.people)*2)
    >>> s=optimization.geneticoptimize(domain,optimization.schedulecost)
    

But the error that I get is:

    
    
    Traceback (most recent call last):
      File "<pyshell#12>", line 1, in <module>
        s=optimization.geneticoptimize(domain,optimization.schedulecost)
      File "optimization.py", line 99, in geneticoptimize
        scores=[(costf(v),v) for v in pop]
      File "optimization.py", line 42, in schedulecost
        for d in range(len(sol)/2):
    TypeError: object of type 'NoneType' has no len()
    

The thing is that the error message appears sometimes and other times not. I
have checked the code and I cannot see where it can be the fault, because pop
never is populated with empty vectors. Any help? Thanks


Answer: You can get `None` in your `pop` list if neither of the conditions in the
`mutate` function are met. In that case the control runs off the end of the
function, which is the same as returning `None`. You need to update the code
to either have only one condition, or to handle a case that doesn't meet
either of them in a separate block:

    
    
    def mutate(vec):
        i=random.randint(0,len(domain)-1)
        if random.random()<0.5 and vec[i]>domain[i][0]:
            return vec[0:i]+[vec[i]-step]+vec[i+1:] 
        elif vec[i]<domain[i][1]:
            return vec[0:i]+[vec[i]+step]+vec[i+1:]
        else:
            # new code needed here!
    

