1

I do not understand why I get a ValueError when I want to transform a calcul string into float. I would like an issue because I am stuck up.

(The aim of my code is to create random equations with increasing level depending on question number.)(I am still a beginner, sorry if my code is unmethodical (and in french too).)

:)

There is my code:

(input)

from random import *

def Numéro_Question():
  global NuméroQuestion
  NuméroQuestion+=1
  print("\t~ Question {} ~\t\n".format(NuméroQuestion))

def Calcul2():
  PremierChiffre=randint(0, NuméroQuestion*5+5)
  Question=str(PremierChiffre)
  for i in range(1,NuméroQuestion+1): 
    SigneDeCalcul=["+","*","-"]
    SigneChoisi=str(choice(SigneDeCalcul))
    x=str(randint(0, NuméroQuestion*5+5))
    Question=Question+SigneChoisi+x
  print(type(Question))
  QuestionNumérique=float(QuestionNumérique)
  QuestionEcrite=Question+" = "
  Question=float
  Réponse=input(QuestionEcrite)


NuméroQuestion=0
Raté=0
while Raté<3:
  Numéro_Question()
  Calcul2()
  print("\n\n")

(output)

(The output changes each time you execute the program because it gives random number)

~ Question 1 ~

<class 'str'>

Traceback (most recent call last):

File "main.py", ligne 26, in <module>

Calcul2()

File "mai,.py", ligne 17, in Calcul2

QuestionNumérique=float(QuestionNumérique) ValueError: could not convert string to float: '3*6'

Marios
  • 26,333
  • 8
  • 32
  • 52
Tom Kuntz
  • 55
  • 1
  • 7
  • Your string does not contain a representation of a float. Did you mean str(‘3.6’)? – quamrana Aug 22 '20 at 10:18
  • `3*6` is not a floating point number. It may be interpreted as an expression but `float()` only accepts floating point literal strings. Use something like `3.6`, or `18`, depending on what you want. – Tom Karzes Aug 22 '20 at 10:20
  • you want to convert an expression string `3*6` to a float. `*` is not a valid float character. Use the dangerous `eval` method – rioV8 Aug 22 '20 at 10:20
  • The code in the question does not produce that error. It actually raises `UnboundLocalError` because `QuestionNumérique` isn't defined anywhere. – ekhumoro Aug 22 '20 at 10:28
  • @tom-karzes Please read the docs https://docs.python.org/3/library/ast.html#ast.literal_eval `ast.literal_eval` cannot evaluate simple operators. – Peaceful James Aug 22 '20 at 10:48

1 Answers1

2

It's because when you use float(my_string) it will only work if my_string can be cast as an actual float. It cannot do the multiplication for you.

Luckily, there is a very useful python function that accepts strings and runs them as code. It is called eval.

For example, eval("12 + 3") will return 15.

Just use eval instead of float, like this:

QuestionNumérique=eval(QuestionNumérique)

In summary, you want to "evaluate" (eval) the Numeric Question, not to "cast" (float) it.

A caveat: As others point out, eval is "unsafe". In general, evaluating arbitrary strings as code is unsafe.

UPDATE: I was thinking about this while eating chips earlier and I had a crazy idea.

OK, so, in Python you can execute shell commands in a subprocess and pipe the results. (os.popen - see here) . Assuming the machine running python has Bash as its shell, we can use Bash's arithmetic expression which (I think) might be easier to safeguard against arbitrary input.

Here is what it would look like:

import os

QuestionNumérique = "117 * 3 + 23 * 3"
shell_command = f"echo $(({QuestionNumérique}))"
piped_shell_result = os.popen(shell_command).read()
stripped_result = piped_shell_result.strip()
result_as_number = float(stripped_result)

This code is still dangerous! You would have to make sure the QuestionNumérique string contains "globally closed" ( and ) brackets, since someone could easily provide evil input like this:

QuestionNumérique = "117 * 3)); mkdir 'YOU HAVE BEEN H&CK3D'; echo $(("

If you can definitely make sure the input string has properly closed brackets then this should be safer than eval, since bash arithmetic expression will only do arithmetic.

Peaceful James
  • 1,807
  • 1
  • 7
  • 16
  • Why is my answer being downvoted? Was that you, @tom-karzes? If so, can you please tell me why. I agree `eval` is unsafe but it is the only thing I know of in Python that can take an arbitrary algebraic expression and calculate the result. The OP wasn't asking for a safe alternative. "Safely" evaluating arbitrary strings is a very very complicated subject, way beyond the scope of a single SO question. – Peaceful James Aug 22 '20 at 10:51
  • You're right, I thought `ast.literal_eval` could handle expressions, but apparently it can't. `eval` is still dangerous, and not something you want to use if it can be avoided. And no, I didn't downvote your answer, it was someone else. It was downvoted before I posted my first comment (which I've removed). – Tom Karzes Aug 22 '20 at 10:52
  • Thanks for the reply. I wish people would explain why they downvote. It makes me want to remove my answer. – Peaceful James Aug 22 '20 at 10:54
  • Some people will downvote any answer that uses `eval`. I don't, but I will comment if I think `eval` can be avoided. – Tom Karzes Aug 22 '20 at 10:55
  • When a tool is forbidden, it makes me want to use that tool even more. Forbidden `eval`... – Peaceful James Aug 22 '20 at 10:56
  • The really bad use case is when `eval` is used in some web server script to evaluate user input. A malicious user can erase your files, delete your database, etc. by entering things like `os.system(...)`. – Tom Karzes Aug 22 '20 at 11:01
  • I completely agree with you. Web servers need to sanitize all user input. I added a comment to my answer saying how executing arbitrary strings is dangerous. I look forward to seeing somebody provide an alternative solution. – Peaceful James Aug 22 '20 at 11:06