I try to understand what assert it is. Every answer say it is debugging stuff. I confused. Should not we use it in production? Should we remove it after the developlement? If assertions are disabled in the Python interpreter. Asserts will be ignored and that can become a security risk? We just use it try to make easy debug on development?
if it is for production. below example i used assert to control not having negative value. Why don't i use if else instead assert?
Thank you in advance.
def get_age(age):
assert age > 0, "Age can't be negative!"
print("your age is"+age)
If nothing else, you lose control over the behavior of get_age using assertions, because they can be disabled at runtime without modifying the code itself. For production usage, be more explicit and raise a ValueError (or, if you deem it worth the effort, a custom subclass of ValueError):
def get_age(age):
if age <= 0:
raise ValueError("Age can't be nonpositive!")
print("your age is" + str(age))
Assert Statements are used when:
the final code is in theory fail safe
the current state of the code is not yet fail safe
E.g. You programm a game with a moving car. Let's say you have a really complicated move() function that works absolutely fine for positive input but in special cases bugs out in weird ways for negative input.
You know this function is going to be fine in the final stage of your game as no function part of the game will every call move() with negative input.
But as you are currently still working on an A.I. driving the car so that you can race against bots - you cannot gurantee that you didn't make a mistake and the A.I. calls the move() function with negative input.
Therefore you put an assert input_arg >= 0 at the beginning of your move() function so that you get notified everytime a wrong input arg was given.
try-except will not catch this as your movement only bugs out but does not raise an exception.
So the assert input_arg >= 0, 'input_arg must be positive!' is just shorthand for if not (input_arg >= 0): raise ValueError('input_arg must be positive!') while signaling that this is not an actual error that can occur in the final stage of the code but only while developing other parts of the game.
It is shorter, can be differentiated from 'real' errors and therefore also automatically stripped away for production code.
Related
I am making a small text-based game in Python. It involves many inputs and so to avoid bugs, there are a few things I have to check every time an input exists. Naturally, to speed up the process I wanted to put the code into a def in order to simplify the writing process. When I put the code in the def, it red underlines the continue and break commands (meaning they are incorrect), and if you run the code using the def name, a Traceback occurs. I have tried putting the def section at the beginning of the program, after the while True: (The program is supposed to run infinitely until a certain action is taken that breaks the loop) I have also made sure to try putting it under any variables referenced and in the loop so that no part of it is not defined and so that everything would work if I were to just put the code in there.
Here is the code I am trying to put into a def.
def input_bugs():
if letter_one.lower() == "done" and total_count == 0:
print("You have to play at least one game!")
continue
elif letter_one.lower() == "done":
break
elif len(letter_one) > 1:
print("Sorry, you gotta pick a single letter, no more. Otherwise, type 'done' to end the game and see your stats.")
continue
Here is the Traceback I get every time I try to run it.
line 20
continue
^^^^^^^^
SyntaxError: 'continue' not properly in loop
At this point, I don't even care if I have to write it out every time, I can just copy and paste. I am simply curious as to why it doesn't work so that I know in the future. In case you could not tell, I am pretty new to programming so I want to learn from any mistake I make. Also sorry if I referred to some things in the wrong way. Hopefully, you understood what I meant.
I'm new to python, and i want to make a simple bank account class, where i want to make a withdraw method, that checks if founds are sufficient and then raises an exception if not.
def withdraw(self, amount):
self.amount -= amount
if amount < self.amount:
raise Exception("Insufficent founds")
print(f"{amount} was withdrawn new balance is {self.amount}")
this is what i have so far, and the logic is working, but i think it would be better with a
try:
# method logic
except Exception:
#handle exception and continue program
my issue is that i can't trigger the exception upon a statement like:
if amount < self.amount:
what do you do in python, when you want to throw an exception upon a certain event, and then continue the program?
The purpose of an exception is for a function to indicate to its caller that it was unable to do whatever thing it is intended to do.
So if you have a function called withdraw, and its purpose is to withdraw funds, if for any reason it cannot withdraw funds, it is appropriate for that function to raise an exception.
If the function identifies a situation where it might be able to withdraw funds, and it performs some extra steps (like prompting the user) and then succeeds, there is no need for any exception. The function still did what it was intended to do. Having the withdraw function take on an extra responsibility of asking for approval seems like a poor design to me, though.
One way to help understand what exceptions you need to raise is to work top down instead of bottom up. That is, start by writing the highest level of what you want your program to do, and then work your way down to the specific details.
If your goal is to simulate an ATM machine, for example, you might start with a top-level function like this:
def main():
while running:
account = select_account()
operation = select_operation()
if operation == 'withdraw':
amount = select_amount()
try:
account.withdraw(amount)
except InsufficientFundsError:
print('Your account has insufficient funds.')
elif operation == 'deposit':
amount = select_amount()
account.deposit(amount)
elif operation == 'done':
running = False
If you start with this "client" code that will use your class, it makes the design for the "service" code (your Account class) much easier. Now you know how you want to be able to use your object, so it's just a matter of writing an object that can be used that way.
When you write a call to a function like account.withdraw(amount), you need to think: How could this fail? What do I want to do if it fails? For example, if the failure reason is that there are insufficient funds, you want to print a message, but you probably don't want to abort the program. But if when you call select_account() the account doesn't exist, then maybe it's OK to fail in a different way. If you fail to handle a possible exception, then the default thing that happens is that your program crashes. That's a good thing: it means that you've found some code that you forgot to write, and if that happens, it's much better for your program to stop running completely than to continue on doing the wrong thing.
A try/except only gets specific errors in python and prevents crashes so the program can keep running (or not if you break). If you don't specify a python's exception it will treat any exception it occurs.
try:
# something
except:
pass #(do nothing)
However if you wan't to treat only specific errors, meaning to catch/ignore only it but others will have other treatments (or even crash on screen) you specify:
div = 0
try:
print(10/div)
except ZeroDivisionError:
print("div can't be zero, change variable")
return # this exits the function
#break # if it is inside a loop
Basically, when you treat an exception the program doesn't stop, it just act like a "known bug" so doesn't crash.
what you could do is exit the function inside your if, but you'd have to do that check before the operation:
def withdraw(self, amount):
if amount < self.amount:
print("Insufficent funds")
return
self.amount -= amount
print(f"{amount} was withdrawn new balance is {self.amount}")
An edit: a better approach would be instead of printing the "insuficient funds" to either write into a log file or returning an output to the program calling this class/function.
The second rule for If-Statements here, which has me confused states that:
If this else should never run because it doesn't make sense, then you must use a die function in the else that prints out an error message and dies, just like we did in the last exercise. This will find many errors.
Here's the code from the last exercise:
def dead(why):
print why, "Good job!"
exit(0)
def start():
print "You are in a dark room."
print "There is a door to your right and left."
print "Which one do you take?"
choice = raw_input("> ")
if choice == ‘left’:
bear_room()
else:
dead(‘You stumble around the room until you starve.’)
Is it essentially saying that you must successfully terminate the program if a condition is not met?
Yes, the idea is:
import sys
def die(msg):
print msg
sys.exit(1)
if condition:
# do stuff
else:
die('This cannot happen!')
You could also use an assert instead, or raise an exception, or anything else that would fail catastrophically. This helps you validate at runtime that the clause you didn't expect to execute, really didn't run.
IMHO you shouldn't get too hung up on how this die is done, exactly. The important point the referred text tries to make is that when you're sure some condition is true, you might as well assert it forcefully so that you can catch runtime bugs.
The guide is trying to show you that you can see the choice that a user gives when it's something that you don't check for in your if statement. If your else looks like:
else:
dead(choice)
You will be able to see what the user input that you didn't expect.
The problem is that the syntax only allows for "Left", when the prompt allows entry of anything. In other words, if I enter "Bob", I will starve. I should be trapping for anything that isn't appropriate and "die"ing at that point (or helping the user make an appropriate decision).
This is an attempt at teaching error handling and die. You would rewrite it such that you allow accurate entry or die (no exit). If you don't choose left, there is no other choice even though the prompt states there are two choices.
If you truly expect only 'left' to be added, a better way of checking for unexpeted input would be
if choice != ‘left’:
dead(‘You stumble around the room until you starve.’)
bear_room()
That way you still validate that the input is what you expected, but it saves indentation and space for the main logic.
I'm pretty new to this whole "programming thing" but at age 34 I thought that I'd like to learn the basics.
I unfortunately don't know any python programmers. I'm learning programming due to personal interest (and more and more for the fun of it) but my "social habitat" is not "where the programmers roam" ;) .
I'm almost finished with Zed Shaws "Learn Python the Hard Way" and for the first time I can't figure out a solution to a problem. The last two days I didn't even stumble upon useful hints where to look when I repeatedly rephrased (and searched for) my question.
So stackoverflow seems to be the right place.
Btw.: I lack also the correct vocabular quite often so please don't hesitate to correct me :) . This may be one reason why I can't find an answer.
I use Python 2.7 and nosetests.
How far I solved the problem (I think) in the steps I solved it:
Function 1:
def inp_1():
s = raw_input(">>> ")
return s
All tests import the following to be able to do the things below:
from nose.tools import *
import sys
from StringIO import StringIO
from mock import *
import __builtin__
# and of course the module with the functions
Here is the test for inp_1:
import __builtin__
from mock import *
def test_inp_1():
__builtin__.raw_input = Mock(return_value="foo")
assert_equal(inp_1(), 'foo')
This function/test is ok.
Quite similar is the following function 2:
def inp_2():
s = raw_input(">>> ")
if s == '1':
return s
else:
print "wrong"
Test:
def test_inp_2():
__builtin__.raw_input = Mock(return_value="1")
assert_equal(inp_1(), '1')
__builtin__.raw_input = Mock(return_value="foo")
out = StringIO()
sys.stdout = out
inp_1()
output = out.getvalue().strip()
assert_equal(output, 'wrong')
This function/test is also ok.
Please don't assume that I really know what is happening "behind the scenes" when I use all the stuff above. I have some layman-explanations how this is all functioning and why I get the results I want but I also have the feeling that these explanations may not be entirely true. It wouldn't be the first time that how I think sth. works turns out to be different after I've learned more. Especially everything with "__" confuses me and I'm scared to use it since I don't really understand what's going on. Anyway, now I "just" want to add a while-loop to ask for input until it is correct:
def inp_3():
while True:
s = raw_input(">>> ")
if s == '1':
return s
else:
print "wrong"
The test for inp_3 I thought would be the same as for inp_2 . At least I am not getting error messages. But the output is the following:
$ nosetests
......
# <- Here I press ENTER to provoke a reaction
# Nothing is happening though.
^C # <- Keyboard interrupt (is this the correct word for it?)
----------------------------------------------------------------------
Ran 7 tests in 5.464s
OK
$
The other 7 tests are sth. else (and ok).
The test for inp_3 would be test nr. 8.
The time is just the times passed until I press CTRL-C.
I don't understand why I don't get error- or "test failed"-meassages but just an "ok".
So beside the fact that you may be able to point out bad syntax and other things that can be improved (I really would appreciate it, if you would do this), my question is:
How can I test and abort while-loops with nosetest?
So, the problem here is when you call inp_3 in test for second time, while mocking raw_input with Mock(return_value="foo"). Your inp_3 function runs infinite loop (while True) , and you're not interrupting it in any way except for if s == '1' condition. So with Mock(return_value="foo") that condition is never satisfied, and you loop keeps running until you interrupt it with outer means (Ctrl + C in your example). If it's intentional behavior, then How to limit execution time of a function call in Python will help you to limit execution time of inp_3 in test. However, in cases of input like in your example, developers often implement a limit to how many input attempts user have. You can do it with using variable to count attempts and when it reaches max, loop should be stopped.
def inp_3():
max_attempts = 5
attempts = 0
while True:
s = raw_input(">>> ")
attempts += 1 # this is equal to "attempts = attempts + 1"
if s == '1':
return s
else:
print "wrong"
if attempts == max_attempts:
print "Max attempts used, stopping."
break # this is used to stop loop execution
# and go to next instruction after loop block
print "Stopped."
Also, to learn python I can recommend book "Learning Python" by Mark Lutz. It greatly explains basics of python.
UPDATE:
I couldn't find a way to mock python's True (or a builtin.True) (and yea, that sounds a bit crazy), looks like python didn't (and won't) allow me to do this. However, to achieve exactly what you desire, to run infinite loop once, you can use a little hack.
Define a function to return True
def true_func():
return True
, use it in while loop
while true_func():
and then mock it in test with such logic:
def true_once():
yield True
yield False
class MockTrueFunc(object):
def __init__(self):
self.gen = true_once()
def __call__(self):
return self.gen.next()
Then in test:
true_func = MockTrueFunc()
With this your loop will run only once. However, this construction uses a few advanced python tricks, like generators, "__" methods etc. So use it carefully.
But anyway, generally infinite loops considered to be bad design solutions. Better to not getting used to it :).
It's always important to remind me that infinite loops are bad. So thank you for that and even more so for the short example how to make it better. I will do that whenever possible.
However, in the actual program the infinite loop is how I'd like to do it this time. The code here is just the simplified problem.
I very much appreciate your idea with the modified "true function". I never would have thought about that and thus I learned a new "method" how tackle programming problems :) .
It is still not the way I would like to do it this time, but this was the so important clue I needed to solve my problem with existing methods. I never would have thought about returning a different value the 2nd time I call the same method. It's so simple and brilliant it's astonishing me :).
The mock-module has some features that allows a different value to be returned each time the mocked method is called - side effect .
side_effect can also be set to […] an iterable.
[when] your mock is going to be
called several times, and you want each call to return a different
value. When you set side_effect to an iterable every call to the mock
returns the next value from the iterable:
The while-loop HAS an "exit" (is this the correct term for it?). It just needs the '1' as input. I will use this to exit the loop.
def test_inp_3():
# Test if input is correct
__builtin__.raw_input = Mock(return_value="1")
assert_equal(inp_1(), '1')
# Test if output is correct if input is correct two times.
# The third time the input is corrct to exit the loop.
__builtin__.raw_input = Mock(side_effect=['foo', 'bar', '1'])
out = StringIO()
sys.stdout = out
inp_3()
output = out.getvalue().strip()
# Make sure to compare as many times as the loop
# is "used".
assert_equal(output, 'wrong\nwrong')
Now the test runs and returns "ok" or an error e.g. if the first input already exits the loop.
Thank you very much again for the help. That made my day :)
Is it bad form to exit() from within function?
def respond_OK():
sys.stdout.write('action=OK\n\n')
sys.stdout.flush() # redundant when followed by exit()
sys.exit(0)
Rather than setting an exit code and exit()ing from the __main__ name space?
def respond_OK():
global exit_status
sys.stdout.write('action=OK\n\n')
sys.stdout.flush()
exit_status = 0
sys.exit(exit_status)
The difference is negligible from a function perspective, just wondered what the consensus is on form. If you found the prior in someone else's code, would you look at it twice?
I would prefer to see an exception raised and handled from a main entry point, the type of which is translated into the exit code. Subclassing exceptions is so simple in python it's almost fun.
As posted in this answer's comments: Using sys.exit also means that the point of termination needs to know the actual status code, as opposed to the kind of error it encountered. Though that could be solved by an set of constants, of course. Using exceptions has other advantages, though: if one method fails, you could try another without re-entry, or print some post-mortem debugging info.
It makes no difference in terms of functionality, but it will likely make your code harder to follow, unless you take appropriate steps, e.g. commenting each of the calls from the main namespace which could lead to an exit.
Update: Note #mgilson's answer re the effect of catching an exception [It is possible to catch the exception that system.exit raises, and thus prevent exit]. You could make your code even more confusing that way.
Update 2: Note #sapht's suggestion to use an exception to orchestrate an exit. This is good advice, if you really want to do a non-local exit. Much better than setting a global.
There are a few cases where it's reasonably idiomatic.
If the user gives you bad command-line arguments, instead of this:
def usage(arg0):
print ... % (arg0,)
return 2
if __name__ == '__main__':
if ...:
sys.exit(usage(sys.argv[0]))
You often see this:
def usage():
print ... % (sys.argv[0],)
sys.exit(2)
if __name__ == '__main__':
if ...:
usage()
The only other common case I can think of is where initializing some library (via ctypes or a low-level C extension module) fails unexpectedly and leaves you in a state you can't reason about, so you just want to get out as soon as possible (e.g., to reduce the chance of segfaulting or printing garbage) For example:
if libfoo.initialize() != 0:
sys.exit(1)
Some might object to that because sys.exit doesn't actually bail out of the interpreter as soon as possible (it throws and catches an exception), so it's a false sense of safety. But you still see it reasonably often.