I made a really simple Dice class and test script in Python as a gist. It is simpler than other dice I found on StackExchange being here and here. I made it simple for modularity and reusability. Granted, there are some quirks such as its max value being determined by a system's word size that may hinder its use. The word size is specific to Python 3.x. Should I make it usable for Python 2.7 and 3.x? I think calling it dice is a misnomer and should be fixed as well. I chose to have exceptions instead of assertions as they can be handled. I am throwing generic exceptions and specific descriptions which I think could be improved by having a more specific exceptions thrown. I think the testing script should probably be class as well. I followed the Sphinx commenting standard but probably did some boilerplate because I think it can be generated. I am interested in getting feedback from others.
dice.py
'''
.. module:: dice
.. moduleauthor:: Jack Bartolone <[email protected]>
'''
import random
import operator
import sys
class Dice(object):
'''
Python 3 dice used to roll a random an integer
between 3 to 2^63 -1 for 64-bit machines and 3 to 2^31-1 for 32-bit machines.
'''
#constants
MINSIDES = 3
#constructors
def __init__(self, sides):
'''
Intializes dice with :param sides
:param sides: sides of the dice
:raises: Exception for out of bounds and non-integer values
'''
self.sides = sides
#range check
sides = property(operator.attrgetter('_sides'))
@sides.setter
def sides(self, value):
'''
Throws exceptions for invalid input.
:param value: setter value
'''
if (value <= self.MINSIDES):
raise Exception('Dice can must have at least three sides')
elif (value > sys.maxsize):
raise Exception('Dice value exceeds max int size')
elif (isinstance(value, int)):
raise Exception('Dice value is not an integer')
else:
self._sides = value
#methods
def roll(self):
'''
Rolls dice.
:returns: pseudorandom integer between 1 and its sides
'''
result = random.randint(1, self.sides)
return result
dicetest.py
'''
.. module:: dice
.. moduleauthor:: Jack Bartolone <[email protected]>
'''
import pytest
import dice
import sys
def testlowerbounds():
'''Test if numbers < 3 throw exception'''
with pytest.raises(Exception):
dice(1)
def testinbounds():
'''Test if sys.maxsize + 1> numbers >= 3 not throw exception'''
with pytest.raises(Exception):
dice(3)
def testupperbounds():
'''Test if sys.maxsize + 1 > numbers throw exception'''
with pytest.raises(Exception):
dice(sys.maxsize + 1)
def testisaninteger():
'''Test if throwing exception for non-integer value'''
with pytest.raises(Exception):
dice('word')
testlowerbounds()
testinbounds()
testupperbounds()
testisaninteger()
#print docs
help(Dice)
elif (isinstance(value, int))
returnsTrue
if value actually is an int, and then you'll raise the "Not an int" exception... but this is such an obvious issue and nobody mentioned it -- so what am I missing? \$\endgroup\$self.sides = sides
in__init__
. In the class,sides
is defined as aproperty
, withoperator.attrgetter('_sides')
as the getter, anddef sides(self, value): self._sides = value
as the setter. That part will work as intended, once the validation code in the setter is fixed; in the constructor,self.sides = sides
will invoke the setter for thesides
property. \$\endgroup\$