# Lottery number picker

This is my first Python project. Let me know how I did, and where I can improve it.

# Random Lottery Number Pick Tool
# It has U.S. national lottos and two from Nebraska
# I'll add more to it later.

import random

# here is a giant body function I wonder if I could make this easier to deal with?
# it is basically my program's menu items.

def workerFunction():
while True:
try: x=input( '\n\n Select an option:\n\n'
'Press 1+Enter for Pick Three\n'
'Press 2+Enter for Pick Five\n'
'Press 3+Enter for Powerball picks\n'
'Press 4+Enter for Mega Millions picks\n'
'Q+Enter to quit \n\n'  )

#This is my end of file logic that kills the program
# Provided you pass Q + Enter

except EOFError: pass
if x.lower().startswith('q'):
print("done")
break

# Otherwise you are passing something else that requires work from my factory!

elif x == '1': print( '\n\n Nebraska Pick Three',
factory.repeater( 3, factory.factoryLogic, [0, 9, 1] ) )
elif x == '2': print( '\n\n Nebraska Pick Five',
factory.factoryLogic( 1, 38, 5 ) )
elif x == '3': print( '\n\n Powerball',
factory.factoryLogic( 1, 55, 5 ),
factory.factoryLogic( 1, 42, 1 ) )
elif x == '4': print( '\n\n Mega Millions',
factory.factoryLogic( 1, 75, 5 ),
factory.factoryLogic( 1, 15, 1 ) )

# My factory class (self is set to something)...
# Not sure how to use it yet outside of the program I know it is like "this" in javascript.

class factory:

# so I am defined self which looks like a struct. I assign it 0 (to shut up the syntax checker)

def __init__( self ): self.a = 0

# My logic module picks numbers based on a start and end position.
# Not sure why I use +1 in the range function.
# And then there are the number of interators I need.

def factoryLogic( startPosi, endPosi, interateNumber ):
a = random.sample( range( startPosi, endPosi+1 ), interateNumber )
a.sort()
return a

# This is a repeater utility I made because pick three needs to be called three times.
# The neat thing is that I am als passing a functions and args as a list and breaking them out.
# I could make it more terse but why? It also returns an appended list. Neat!

def repeater( times, f, functionArgs ):
return_list = []
for i in range( times ): return_list.append( f( functionArgs[0], functionArgs[1], functionArgs[2]  ) )
return return_list

# This name main holds my worker function. I call it workerFunction! Amazing, yes?
if __name__ == '__main__':
workerFunction()

• Obligatory link to style guide: legacy.python.org/dev/peps/pep-0008 – jonrsharpe Nov 1 '14 at 21:48
• Is this written for Python 2.x or 3.x? – jonrsharpe Nov 1 '14 at 22:17
• I think it would work on both? I don't think I used anything version specific. – Frank Tudor Nov 1 '14 at 22:29
• input and EOFError suggests you're not using raw_input for 2.x, which is bad form (and your class doesn't inherit from object, so will be old-style in 2.x). – jonrsharpe Nov 1 '14 at 22:41
• @jonrsharpe Oh I see...I'm going through your notes. Thanks for giving so much feedback. – Frank Tudor Nov 1 '14 at 22:48

You are not compliant with the style guide. For example:

• x and a aren't good variable names;
• You don't follow the naming convention (e.g. factory should be Factory and workerFunction should be worker_function);
• The inline comments should be replaced with docstrings;
• Extraneous whitespace (e.g. for i in range( times ): should be for i in range(times):); and
• Compound statements (e.g. except EOFError: pass should be on two lines).

The Pythonic replacement for lots of elif clauses is a dictionary. There are numerous ways to implement this, e.g.:

lotteries = {1: {'name': 'Nebraska Pick Three',
'func': factory.repeater
'args': (3, factory.factoryLogic, [0, 9, 1])},
...}


This simplifies the printout at the start:

print("Make a choice: ")
for key in sorted(lotteries):
print("{0}. {1}".format(key, lotteries[key][name]))


and input validation:

if int(x) not in lotteries:


and the output:

lottery = lotteries[x]
print lottery['func'](*lottery['args'])


If you do want to stick with the current text block, Python does have multiline strings:

s = """Three quote marks
makes a string
multiline."""


For help on formatting, see Avoiding Python multiline string indentation.

The class implementation seems pointless - you have an __init__ that does very little (self.a is never referenced again), and two instance methods that don't need any class or instance attributes. If you want an OOP approach, I would be tempted by something like:

class Lottery:

def __init__(self, rules):
self.rules = rules

def draw(self):
...


You can then put instances into the dictionary:

lotteries = {1: {'name': 'Nebraska Pick Three',
'lottery': Lottery(...)},
...}


and the code gets even simpler:

lottery = lotteries[x]['lottery']
lottery.draw()


You will have to come up with a method for representing the different rules. I would suggest having a Boolean for when repeat draws are allowed, rather than a sample, instead of calling a separate method as you do now. Different lotteries could alternatively be subclasses, rather than instances, of Lottery.

Here's an example implementation (note: for Python 3.x):

import random

class Lottery:
"""Simulate a specified lottery."""

def __init__(self, name, rules):
"""Define the lottery name and draw rules."""
self.name = name
self.rules = rules

def draw(self):
"""Simulate a single draw of the lottery."""
output = []
for rule in self.rules:
output.extend(self._draw_rule(rule))
return output

def _draw_rule(self, rule):
"""Helper function to draw a single rule."""
if rule.get('repeats', False):
output = [random.randint(rule.get('min', 1), rule['max'])
for _ in range(rule.get('picks', 1))]
else:
output = random.sample(range(rule.get('min', 1), rule['max']+1),
rule.get('picks', 1))
return output

lotteries = {1: Lottery('Nebraska Pick Three',
[{'picks': 3, 'max': 9, 'repeats': True}]),
[{'picks': 5, 'max': 38}]),
3: Lottery('Powerball',
[{'picks': 5, 'max': 75}, {'max': 15}])}

def play():
"""Allow the user to choose a lottery and simulate a draw."""
print("Select a lottery:")
for key in sorted(lotteries):
print("{0}: {1.name}".format(key, lotteries[key]))
while True:
try:
choice = int(input("Make a choice: "))
except ValueError:
else:
if choice in lotteries:
break
print("Not a valid choice.")
print(lotteries[choice].draw())

if __name__ == "__main__":
play()


And its output:

>>> play()
Select a lottery:
3: Powerball
Make a choice: foo
Make a choice: 42
Not a valid choice.
Make a choice: 1
[1, 7, 9]
>>> play()
Select a lottery:
3: Powerball
Make a choice: 2
[34, 33, 20, 7, 35]
>>> play()
Select a lottery:

Here I have used a simple dictionary implementation of the rules, with sensible defaults to minimise the input needed.
• @jonsharpe Question: In my code there is something I am doing but I don't know what it means. Simplified example random.sample(range(1,50+1),5) That +1 (what does that mean?) – Frank Tudor Nov 1 '14 at 23:04
• @FrankTudor why are you doing it if you don't know what it means?! +1 is necessary because range is "half-open", i.e. excludes the final argument (e.g. range(10) gives 0, 1, ..., 8, 9 but not 10). – jonrsharpe Nov 1 '14 at 23:06
• @FrankTudor not quite, it's only half-open - the lower bound is included, so range(1, 50) would give 1-49 inclusive. See e.g. here for why. Rather than writing stuff without knowing why, work through a tutorial to get a more structured introduction - docs.python.org/3/tutorial/controlflow.html#the-range-function. – jonrsharpe Nov 1 '14 at 23:13