Python: NameError: name "string" is not defined, not via input() - python-3.x

The function below checks to see if the first 9 digits of string (n) equate to the 10th character (an integer from 1-9 or X for 10).
def isISBN(n):
checkSum = 0
for i in range(9):
checkSum = checkSum + (eval(n[i])*(i+1))
if checkSum%11 == eval(n[9]) or (checkSum%11 == 10 and n[9] == 'X'): return True
else: return False
When I run the function for n='020103803X' I get an error:
NameError: name 'X' is not defined
I've searched for this problem and found that most people's issues were with input() or raw_input(), but as I am not using input(), I'm confused as to why I can't test if a character is a specific string. This is my first post as Python beginner, please tell if I'm breaking rules or what extra info I should include.

The problem is with your use of eval: eval('X') is the same as doing X (without the quotes). python sees that as a variable reference, and you have no variable named X.
There is no reason to use eval here. What are you hoping to accomplish? Perhaps you should be checking to see if the character is a digit?
if checkSum%11 == n[9].isdigit() or (checkSum%11 == 10 and n[9] == 'X'): return True

You're trying to get a response from
eval('X')
This is illegal, as you have no symbol 'X' defined.
If you switch the order of your if check, you can pass legal ISBNs. However, it still fails on invalid codes with an X at the end.
def isISBN(n):
checkSum = 0
for i in range(9):
checkSum = checkSum + (eval(n[i])*(i+1))
if (checkSum%11 == 10 and n[9] == 'X') or \
checkSum%11 == eval(n[9]):
return True
else:
return False
Note also that you can short-cut that return logic by simply returning the expression value:
return (checkSum%11 == 10 and n[9] == 'X') or \
checkSum%11 == eval(n[9])

Eval is not the proper usage, nor is the way you use it correct. For example, see Wikipedia which shows the use. You probably want to use a try: except: pair.
try:
int(n[i]
except:
print "this character is not a digit"
A call to eval is sometimes used by inexperienced programmers for all
sorts of things. In most cases, there are alternatives which are more
flexible and do not require the speed penalty of parsing code.
For instance, eval is sometimes used for a simple mail merge facility,
as in this PHP example:
$name = 'John Doe';
$greeting = 'Hello';
$template = '"$greeting,
$name! How can I help you today?"';
print eval("return $template;");
Although this works, it can cause some security problems (see ยง
Security risks), and will be much slower than other possible
solutions. A faster and more secure solution would be changing the
last line to echo $template; and removing the single quotes from the
previous line, or using printf.
eval is also sometimes used in applications needing to evaluate math
expressions, such as spreadsheets. This is much easier than writing an
expression parser, but finding or writing one would often be a wiser
choice. Besides the fixable security risks, using the language's
evaluation features would most likely be slower, and wouldn't be as
customizable.
Perhaps the best use of eval is in bootstrapping a new language (as
with Lisp), and in tutoring programs for languages[clarification
needed] which allow users to run their own programs in a controlled
environment.
For the purpose of expression evaluation, the major advantage of eval
over expression parsers is that, in most programming environments
where eval is supported, the expression may be arbitrarily complex,
and may include calls to functions written by the user that could not
have possibly been known in advance by the parser's creator. This
capability allows you to effectively augment the eval() engine with a
library of functions that you can enhance as needed, without having to
continually maintain an expression parser. If, however, you do not
need this ultimate level of flexibility, expression parsers are far
more efficient and lightweight.

Thanks everyone. I don't know how I didn't think of using int(). The reason I used eval() was because the past few programs I wrote required something like
x = eval(input("Input your equation: "))
Anyways the function works now.
def isISBN(n):
checkSum = 0
for i in range(9):
checkSum = checkSum + (int(n[i])*(i+1))
if n[9] == 'X':
if checkSum%11 == 10: return True
else: return False
elif checkSum%11 == int(n[9]): return True
else: return False

Related

Palindrome coding and logic

When investigating if a string is Palindrome, cant i just take the reverse of the reversed string? whats wrong with my code?
def reverse(s):
return s[::-1]
def isPalindrome(s):
if (s == reverse(reverse(s))):
return True
else:
return False
reverse(reverse(s)) always equals s. You want to check whether s equals reverse(s):
def is_palindrome(s):
return s == reverse(s)
It depends on what you are aiming to do. If you just want to have the logic implemented, then your version would work (yet you still have to correct your bug there, it's if (s == reverse(s)) and not reversing the reversed version); however, if that's the task which was given to you in your coding interview, then you're not really doing it right, because of:
You're using an integral API call to reverse the string (and hence you're not implementing isPalindrome);
The time complexity of your version will be much worse than the
complexity of isPalindrome algorithm per se.

How to exactly compare object type in Python 3?

In Python 2 code I had something like:
...
elif type(value) == datetime.date:
return transform_date(value)
elif type(value) == datetime.datetime:
return transform_datetime(value)
...
Now, if I follow advise of avoiding "antipattern", I should replace it with isinstance.
isinstance(value, datetime.date)
And there will be a problem, because then correctness will depend on which type I check first.
I am well aware of more than 10 years old How to compare type of an object in Python? and What's the canonical way to check for type in Python? , but this question differs in two important ways:
It's about Python 3, idioms may be different
I am interested in exact check, because the transformation depends on the exact type (otherwise it will be lossy)
Exact match: For example, obj.__class__ or type(obj) matches.
As in Python there is only one obvious way to do it, the answer to this question should not depend on opinion that much.
If you really want strict per-exact-class match, the pythonic solution hasn't changed since py2: use a dict
dispatch = {
datetime.date: transform_date,
datetime.datetime: transform_datetime,
# etc
}
transform = dispatch.get(type(value), some_default_func)
transform(value)

Random number for variable when called

I'm fairly new in programming and python in general. I already learned for, if's, and while, and I'm trying to make a pretty decent rpg/dungeon game. I'm wondering how do I make a variable have a random number when used for the "damage".
I used randint(a, b) for the variable but it is only random once.
This is a creature example:
name_slime = "Red Slime"
slime_hp = randint(100, 200)
slime_atk = randint(1, 2)
slime_crit = 60
slime_evade = 10
slime_exp = randint(1, 5)
slime_chance = randint(50, 60)
And these will get called on another function:
def battle(enemy_name, enemy_hp, enemy_atk, enemy_crit, enemy_evade, enemy_exp):# Battle sequence
while enemy_hp > 0:
global player_hp, potion_count
print(f"{enemy_name}'s Stats:")
print(f"HP: {enemy_hp} Power: {enemy_atk} Crit Chance: {enemy_crit}% Evade: {enemy_evade}%\n")
print("Your Stats:")
....
print("Action:\n1. Attack\n2. Parry\n3. Use Potion")
choice = input("> ")
if choice == "1":
....
elif choice == "2":
....
elif choice == "3":
....
print(f"You gained {enemy_exp} EXP")
exp_check(enemy_exp)
print(f"Your EXP is now {player_exp}/{exp_limit}")
P.S: I scraped a lot of things in this code because it's quite long for there is a lot of lines for the calculation.
Here's the full code if anyone can help:
https://pastebin.com/iFMZyY4z
I'll just take the exp for this case.
In the variable "slime_exp" it should give a number between 1 and 5. But when i tried fighting the creature multiple times in one run (not exiting the terminal) it always give the same amount of exp each time.
I'm thinking that the randint(1, 5) is defined when the script is run and not when the variable is used.
This happens for all variable that have randint()
How can I make it so that it will be random when the variable is used?
Using the choice method is an alternative that might be suitable for you:
slime_exp = range(1,6) # slime_exp is now a variable of 'range' type!
For more infomation about the range() function see range in Python.
And whenever you use the variable slime_exp you can use choice(slime_exp).
choice is a function that returns a pseudo-random value from a list, range, tuple, string. For more information see choice in Python.
Note that, to use choice you have to "import" it first by doing one of these options:
from random import * (not very advised)
import random (in this case you should use random. choice instead of just choice)
or from random import choice
And when you want to use the variable slime_exp, you just use choice(slime_exp) instead of slime_exp. Note that this may be a good option if you don't need to use the same value several times since the exact value of choice(slime_exp) is "pseudo-randomized" everytime you use it.
You can write a function to update the slime_exp variable and call it when needed, like below.
slime_exp = randint(1, 5)
def update_slime_exp():
global slime_exp
slime_exp = randint(1, 5)
update_slime_exp()
print(slime_exp)
However, changing the state of global variable is not a good programming practise. Instead get the random number as return value and use it when needed instead of storing it in a global variable.

Why would Python execute my code correctly while I do have an obvious error?

While I was practicing some if statements in Python, I ran into a weird problem that made me confused about how exactly does Python execute programs.
referring to the following code:
x = 63
if x >= 0 and x <= 9:
print("X is One digit, and it is: " , x)
elif x >= 10 and x <= 99:
print("X is Two digits, and it is: " , x) # the error begins here
elif x >= 100 and x <= 999:
print("X is Three digits, and it is: " + x)
else:
print("X is a negative number, and it is: " + x)
If the value of x is between 0-99 the program will work correctly. Though as obvious it contains an error. why I still get an output. This made me really curious about how Python executes programs!
The program is syntactically valid Python, but the + operation will fail between a string and an int, but is allowed between a string and a string.
The comma, on the other hand, is valid, since print works on any number of objects. You can tell by calling help on a function object to see what arguments it accepts. print will add an extra space between arguments by default, so that space after the colon in the string is probably also a mistake.
Python doesn't force static type checking. If you're used to a statically-typed language, this may be surprising. This kind of type error doesn't happen until runtime, but doesn't happen at all if the branch is not executed. You can use an external static type checker like mypy, plus Python's type annotation syntax to catch this kind of type error even before running the program.
Many bugs in Python are hard to detect until the code is executed. That's why you should write tests to execute all of your code. Syntax errors will still show up before you run the program though.
Python also allows chained comparisons, so
0 <= x <= 9
would be more pythonic.

Readable, controllable iterators?

I'm trying to craft an LL(1) parser for a deterministic context-free grammar. One of the things I'd like to be able to use, because it would enable much simpler, less greedy and more maintainable parsing of literal records like numbers, strings, comments and quotations is k tokens of lookahead, instead of just 1 token of lookahead.
Currently, my solution (which works but which I feel is suboptimal) is like (but not) the following:
for idx, tok in enumerate(toklist):
if tok == "blah":
do(stuff)
elif tok == "notblah":
try:
toklist[idx + 1]
except:
whatever()
else:
something(else)
(You can see my actual, much larger implementation at the link above.)
Sometimes, like if the parser finds the beginning of a string or block comment, it would be nice to "jump" the iterator's current counter, such that many indices in the iterator would be skipped.
This can in theory be done with (for example) idx += idx - toklist[idx+1:].index(COMMENT), however in practice, each time the loop repeats, the idx and obj are reinitialised with toklist.next(), overwriting any changes to the variables.
The obvious solution is a while True: or while i < len(toklist): ... i += 1, but there are a few glaring problems with those:
Using while on an iterator like a list is really C-like and really not Pythonic, besides the fact it's horrendously unreadable and unclear compared to an enumerate on the iterator. (Also, for while True:, which may sometimes be desirable, you have to deal with list index out of range.)
For each cycle of the while, there are two ways to get the current token:
using toklist[i] everywhere (ugly, when you could just iterate)
assigning toklist[i] to a shorter, more readable, less typo-vulnerable name each cycle. this has the disadvantage of hogging memory and being slow and inefficient.
Perhaps it can be argued that a while loop is what I should use, but I think while loops are for doing things until a condition is no longer true, and for loops are for iterating and looping finitely over an iterator, and a(n iterative LL) parser should clearly implement the latter.
Is there a clean, Pythonic, efficient way to control and change arbitrarily the iterator's current index?
This is not a dupe of this because all those answers use complicated, unreadable while loops, which is what I don't want.
Is there a clean, Pythonic, efficient way to control and change arbitrarily the iterator's current index?
No, there isn't. You could implement your own iterator type though; it wouldn't operate at the same speed (being implemented in Python), but it's doable. For example:
from collections.abc import Iterator
class SequenceIterator(Iterator):
def __init__(self, seq):
self.seq = seq
self.idx = 0
def __next__(self):
try:
ret = self.seq[self.idx]
except IndexError:
raise StopIteration
else:
self.idx += 1
return ret
def seek(self, offset):
self.idx += offset
To use it, you'd do something like:
# Created outside for loop so you have name to call seek on
myseqiter = SequenceIterator(myseq)
for x in myseqiter:
if test(x):
# do stuff with x
else:
# Seek somehow, e.g.
myseqiter.seek(1) # Skips the next value
Adding behaviors like providing the index as well as value is left as an exercise.

Resources