A stacktrace would've helped, but presumably the error is:
materials = 1 + (level * 1)
‘level’ is a string, and you can't do arithmetic on strings. Python is a dynamically-typed language, but not a weakly-typed one.
level= raw_input('blah')
try:
level= int(level)
except ValueError:
# user put something non-numeric in, tell them off
In other parts of the program you are using input(), which will evaluate the entered string as Python, so for “1” will give you the number 1.
But! This is super-dangerous — imagine what happens if the user types “os.remove(filename)” instead of a number. Unless the user is only you and you don't care, never use input(). It will be going away in Python 3.0 (raw_input's behaviour will be renamed input).
Here is an example of a Type Error and how to fix it:
# Type Error: can only concatenate str (not "int") to str
name = "John"
age = 30
message = "My name is " + name + " and I am " + age + " years old."
# Fix:
message = "My name is " + name + " and I am " + str(age) + " years old."
In the above example, the error message says that we're trying to concatenate a string and an integer which is not possible. So, we need to convert the integer to string using str() function to fix the error.
You can use a try-except to catch the error, and throw for example another one:
def prefill(n,v):
try:
n = int(n)
except ValueError:
raise TypeError("{0} is invalid".format(n))
else:
return [v] * n
For example:
>>> prefill(3,1)
[1, 1, 1]
>>> prefill(2,"abc")
['abc', 'abc']
>>> prefill("1", 1)
[1]
>>> prefill(3, prefill(2,'2d'))
[['2d', '2d'], ['2d', '2d'], ['2d', '2d']]
>>> prefill("xyz", 1)
Traceback (most recent call last):
File "<stdin>", line 1, in prefill
ValueError: invalid literal for int() with base 10: 'xyz'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in prefill
TypeError: xyz is invalid
You do not per se need to specify an exception to raise. In case you want to re-raise the exception, simply writing raise is sufficient. You can furthermore specify a tuple of exceptions to catch, and make v optional here, for example:
def prefill(n,v=None):
try:
n = int(n)
except (TypeError, ValueError):
raise TypeError("{0} is invalid".format(n))
else:
return [v] * n Use the raise keyword to raise an exception, rather than return it. raise is used to generate a new exception:
>>> raise TypeError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError
In this example I raised the TypeError by using the exception class directly. You can also create instances of the error like this:
>>> t=TypeError()
>>> raise t
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError
Doing it that way allows various properties to be set on the object before using raise. The problem posted includes this requirement:
When throwing a TypeError, the message should be n is invalid, where you replace n for the actual value passed to the function.
That is an example of a situation where it is necessary to create an instance of the error and set a message property on it before using raise. That can still be done in one line:
>>> raise TypeError("foo")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo
To catch exceptions, use a try-except block, rather than an if block:
x = 'hello, world!'
try:
y = x / 2
except TypeError as e:
e.args = (*e.args, str(x) + " is not valid")
raise
This will raise an error:
TypeError: ("unsupported operand type(s) for /: 'str' and 'int'", 'hello, world! is not valid')
note that you can check the datatype of a variable using type():
>>> x = 5
>>> type(x)
<class 'int'>
>>> if type(x) == int:
... print("it's an int")
...
it's an int
Also, the code sample could be simplified to:
return [v for _ in range(n)]