I'm trying to demonstrate how yield in Python is used. I want to demonstrate that through an example.
The example will ask the user to enter yes or no, and increase the counter n by 1 every time yes is entered.
The part I want to show how yield works is when the user calls the function again, and getting an updated value of the number of times. For instance, if return was used, and the user runs the script again, it will start from scratch and the number of times would be 1. Rather, I want the user to get 1,2,3,...etc, that is the number of times yes was entered.
The issue here is how to use yield to demonstrate such example. In the code I wrote below, I always get a generator object returned rather than the value of n. How can I get the integer value n returned instead?
def yes_times(answer):
n = 0
if answer == 'yes':
n = n + 1
yield n
answer = raw_input('"yes" or "no": ')
times = yes_times(answer)
print 'You answered yes ' + str(times) + ' times'
Thanks.
I'm trying to demonstrate how yield in Python is used.
... example problem ...
The issue here is how to use yield to demonstrate such example.
You're doing this backwards.
Don't demonstrate how yield is used by picking some arbitrary example, which is unrelated to generator behaviour, and try implementing it using yield.
Instead, choose something that yield is good at, and implement that - ideally with a comparable non-yield implementation so you can show the benefits.
Simple example: toy implementation of range
def naive_range(begin, end):
i = begin
result = []
while i < end:
result.append(i)
i = i + 1
return result
def generate_range(begin, end):
i = begin
while i < end:
yield i
i = i + 1
Now, can you name the drawbacks of the naive implementation? When will it be significantly worse than the generator, and why?
For your example, you can try:
def yes_times(answer = None):
count = 0
while True:
if answer=="yes":
count += 1
answer = yield count
else:
answer = yield count
gen = yes_times()
gen.next()
while True:
answer = raw_input('"yes" or "no": ')
print 'You answered yes ' + str(gen.send(answer)) + ' times'
The problem is that you're trying to use the generator as if it were a function. I strongly recommend that you go through a tutorial on line until you grasp the difference -- after all, that's how I quit making exactly this mistake. :-)
Your statement
times = yes_times(answer)
instantiates the generator, which is what you get when you print. Instead, you need to either use the next function ...
times = next(yes_times(answer))
... or properly employ a generator that does what you need. The general use of a generator is to recognize it as a stream of data, something like
for times in yes_times(answer):
However, this requires that you update your generator to get the input itself.
Is that enough to get you moving?
Related
I am currently studying Software Development as a beginner and I have a task in my programming class to calculate and display a factorial using a loop. I've been given the pseudo-code and have to translate it into true code and test it in the REPL to make sure it returns the expected results.
I almost have it but I've run into two issues that I just can't seem to resolve.
1) The function is returning an extra line of "None" after the calculation and
2) The answer is displaying over multiple lines when I want it to display on a single line.
My current code (which does return the correct answer) is as follows:
def calcFactorial(number):
factorial = 1
print(str(number)+"! =", number)
for count in range (1, number):
if number-count > 0:
factorial = factorial*number-count
print("x", str(number-count))
factorial = factorial*number
print("=", factorial)
When I test, using 3 for example, the REPL returns the following:
>>> print(calcFactorial(3))
3! = 3
x 2
x 1
= 12
None
So I have the correct answer but with an extra line of "None" which I would like to remove (I believe it has something to do with the print function?) and I don't know how to format it correctly.
Any help would be much appreciated.
your function calcFactorial(3) prints already, so you shouldn't call it with
print(calcFactorial(3))
just call it with
calcFactorial(3)
without the print function.
you might think about calling the function calc_and_print_factorial() in order to make it clear, that this function does already the printing
Regarding your second question:
Blockquote
2) The answer is displaying over multiple lines when I want it to display on a single line.
You can fix it by using a single print statement:
def calcFactorial(number):
factorial = 1
string = str(number) + "! = " + str(number)
for count in range (1, number):
if number-count > 0:
factorial = factorial*(number-count)
string = string + " x " + str(number-count)
factorial = factorial * number
print(string + " = " + str(factorial))
This will give you:
IN: calcFactorial(3)
OUT: 3! = 3 x 2 x 1 = 6
On a side note: you might want to think of how to implement this recursively. Maybe that comes later in your class but this would be one of the first go-to examples for it.
Adding to the blhsing's answer, you should choose between these built-in ways to print the "returned" value.
First way:
def calcFactorial(number):
... # <- Your function content
return factorial
Then, call your function with a print() to get the explicitly returned value, as you can see in the return factorial line. See this reference for more details:
print(calcFactorial(3))
Second way:
Having the same function definition with its return statement, just call the function with its instance statement:
calcFactorial(8)
By default, python will print the returned value without a print()
Third way:
Just call the function (without the explicit return statement, this will return a "None" (null-like) value by default), using the print() method. Do NOT use print() inside another print().
Your calcFactorial function does not explicitly return a value, so it would return None by default, so print(calcFactorial(3)) would always print None.
You should make the calcFactorial function return factorial as a result at the end:
def calcFactorial(number):
factorial = 1
print(str(number)+"! =", number)
for count in range (1, number):
if number-count > 0:
factorial = factorial*number-count
print("x", str(number-count))
factorial = factorial*number
print("=", factorial)
return factorial
So I have the correct answer but with an extra line of "None" which I would like to remove
You are printing the return value from your function. In this case, you haven't specified a return value with a return statement so the function automatically returns None.
To fix the problem, you should return a value from your function. Note that you don't need to call print() for final answer because the REPL already does this for you.
Note that the REPL will automatically print the return value for you, so you can just type calcFactorial(3) instead of print(calcFactorial(3)).
Additionally, you are not getting the correct answer. You should get 6 instead of 12. It looks like you are trying to count down from number and multiplying each number together. You can get the same result by counting up from 1. This will lead to much simpler code and avoid the error.
If you want to understand why your code isn't doing the correct thing, look closely at factorial = factorial*number-count and think about the order of operations that are used to calculate the result here.
Given 1 to 100 numbers, for multiples of 3 it should print "he" ,for multiples of 5 it should print "llo" ,for both multiples of 3 and 5 it should print "hello".
This is what I have:
for i in range (1,100):
if(i%3==0):
print("he")
elif(i%5==0):
print("llo")
elif(i%3==0 and i%5==0):
print("hello")
How would I do this recursively?
How about the code below?
def find_multiples(current, last_num=100):
# Base Case
if current > last_num:
return
result = ""
if current % 3 == 0:
result += "he"
if current % 5 == 0:
result += "llo"
if result:
print(f"{current}: {result}")
find_multiples(current+1, last_num)
find_multiples(1)
Base case is if current reaches last_num or the maximum number you'd like to check.
Here is a general outline for doing simple recursive things in python:
BASE_CASE = 1 #TODO
def f(current_case):
if current_case == BASE_CASE:
return #TODO: program logic here
new_case = current_case - 2 #TODO: program logic here ("decrement" the current_case somehow)
#TODO: even more program logic here
return f(new_case) + 1 #TODO: program logic here
Of course, this doesn't handle all possible recursive programs. However, it fits your case, and many others. You would call f(100), 100 would be current_value, you check to see if you've gotten to the bottom yet, and if so, return the appropriate value up the call stack. If not, you create a new case, which, in your case, is the "decrement" logic normally handled by the "loop" construct. You then do things for the current case, and then call the function again on the new case. This repeated function calling is what makes it "recursive". If you don't have an "if then" at the beginning of the function to handle the base case, and somewhere in the function recall the function on a "smaller" value, you're probably going to have a bad time with recursion.
This recursive function prints multiples of a number! hope it helps
def multi(n,x):
if x == 12:
print(n*x)
else :
print(n*x,end =",")
multi(n,x+1)
print(multi(4,1));
I'm starting to learn basic programming with python, and as I encountered permutations, I wanted to see if I could (for fun) brute force a userinput..
It seems to work for short PW's, but my counter seems to have a flaw, or do I miss something about permutations nature? ...the counter shows the same number each time, for any PW of the same length...
Also, there's a memory error for longer PW's, but I thought I ruled that out by using a generator(yield) instead of a list.. or didn't I?
Thanks for your Help
import string
from itertools import permutations
characters = string.ascii_letters
user_pw = input('Set your PW')
attempts = 0
done = False
while True:
def Generator():
guess_storage = [''.join(p) for p in permutations(characters, len(user_pw))]
for current in guess_storage:
yield current
for guess in Generator():
attempts += 1
if guess == user_pw:
print('SUCESS: ')
print('Password: ', guess)
done = True
if done == True:
break
print('Attempts: ', attempts)
I'll start with the memory error. By doing:
guess_storage = [''.join(p) for p in permutations(characters, len(user_pw))]
you are 'gluing' all the generated permutations back into a single list before then iterating over that list. this is where you run out of memory. Instead, try making a generator to be iterated over:
guess_storage = (''.join(p) for p in permutations(characters, len(user_pw)))
Or don't use a generator, and just join each permutation as it is yielded (see below).
Next, you are doing this all in a while loop (including the generator), but this is unnecessary and inefficient. Just build the Generator once, then when you are looping over every guess from the generator, just break out when it finds a match.
A (shorter) way of writing what I think you intended is:
def Generator():
for current in permutations(characters, len(user_pw)):
yield ''.join(current)
for guess in Generator():
print("Guessing", guess) # To show the guesses in action
attempts += 1
if guess == user_pw:
print('SUCCESS: ')
print('Password: ', guess)
print('Attempts: ', attempts)
break
You can also avoid manually tracking the attempts counter by using:
for attempts, guess in enumerate(Generator()):
I'm new to Python, and I'm playing around with recursive functions just for practice.
I made the following algorithm which takes a number as x and halves it until it is equal to 1. n is the number of times x has been halved.
def binary(x, n = 0):
print(n,":",x)
x = x // 2
n += 1
if x > 0:
binary(x, n)
return x
return x
I'm trying to make a loop that will call binary() with multiple values for x. I want my step to be doubled each iteration. I have it working with a while loop like the one below.
i = 1
while i < 1000000000:
print("when x is", i, ":")
binary(i)
i += i
For some reason though, I can't seem to achieve the same thing with a For loop. Here's what I have now.
for i in range(1,1000):
print("when x is", i, ":")
binary(i)
i += i
In the code above, i += i does not seem to effect the i in my header. I know that range() takes a third parameter called step, but I've tried this:
for i in range(1,1000, i += i):
# statements
This gives me a name error, and says "i is not defined".
Most of my experience with programming is in JavaScript and C#. In both of those languages I wouldn't of had any trouble doing this.
How would I get this to work in a For loop using Python 3?
The third parameter of range is indeed step. But you should use it with a computed value like:
for i in range(1,1000,2):
#statements
The reason why your i += i didn't worked is because, under the hood, the for-loop is executing something similar to i = next(...) at the end of an iteration, overiding your last increment.
Edit
You can achieve the desired effect using a generator, but it kinda kills the "trying to avoid while-loops" thing:
def myrange(start, stop):
i = start
while i < stop:
yield i
i += i
for i in myrange(1, 1000):
print(i)
Anyway, while-loops are perfectly valid constructs and I’d personnally go with one in this case. Do not forget that the for-loop has a rather different semantic in python than in both languages you’re used to. So trying to use a for-loop because you are able to do so with javascript seems like a bad idea if all what you need is a while-loop.
range can step by a fixed amount, but not a variable amount. Use a while-loop to increment i by i:
i += i
You could replace the while-loop with an iterator, such as:
import itertools as IT
for i in (2**i for i in IT.count()):
if i >= 1000000000: break
print("when x is", i, ":")
binary(i)
but I don't think this has any advantage over a simple while-loop.
If all you're doing is doubling i, then why not just raise it to the power?
for p in range(int(1000000000**0.5)):
print(binary(2**p)
Python newbie here, so bear with me...
I'm attempting to utilize the often poorly written Codeacademy Python tutorial, wherein the exercise challenge is to:
Write a function called digit_sum that takes a positive integer n as
input and returns the sum of all that number's digits.
For example: digit_sum(1234) should return 10 which is 1 + 2 + 3 + 4.
(Assume that the number you are given will always be positive.)
So, my attempt to solve for this challenge was writing the following code:
userInput = raw_input("Please enter your number string here: ")
n = userInput
lst = list(n)
userNumbers = []
def digit_sum(n):
for i in lst:
b = int(i)
userNumbers.append(b)
numsum = sum(userNumbers)
return numsum
print "This is your total: %s" % digit_sum(n)
In the console for this exercise, everything seems to work as expected:
Please enter your number string here: 123
This is your total: 6
None
However, the interpreter returns this error (despite the console seeming to work properly):
Oops, try again. Your function fails on digit_sum(434). It returns 18
when it should return 11.
Why in the heck is this error being returned? Can anyone elucidate what's happening here?
Unfortunately, there's no one to ask at Codeacademy, you have to just post a question which only other students read, so not getting any insights there.
You do not understand variable scoping or types. That's the "problem". (It's not really a problem, since you're learning.)
What's expected
The question wants you to provide a function digit_sum that accepts a number, and returns a number. In this case it would accept 434 and return 18. That is, digit_sum(434) = 18.
What you're doing
Reading from the keyboard (raw_input) and printing (print) are not related to the question.
If we remove the non-function parts of your solution, we have:
def digit_sum(n):
for i in lst:
b = int(i)
userNumbers.append(b)
numsum = sum(userNumbers)
return numsum
When we try to submit this, some problems become apparent:
lst is not defined.
userNumbers is not defined.
You expect a string (str) and not a number (int)
Addressing the issues
We have to move the definition of userNumbers and lst into the
function digit_sum. If they are set at the "top" level, they are
not reset each time you call the function, and that's why Codecademy
isn't getting the correct answer.
The code they use to check yours is something like:
if digit_sum(434) != 18:
print "It should have been 18, we got %s" % digit_sum(434)
They have many such calls, and each subsequent call does not change
the value of lst or userNumbers.
This masks another error: your digit_sum function accepts a string. That is, calling it (after incorporating fixes from #1 above) as digit_sum(434) which is what Codecademy does, will result in an error, since you actually expect it be called as digit_sum("434").
A possible solution
The following function is based on yours, but is altered with the advice above:
def digit_sum(n):
lst = list(str(n))
userNumbers = []
for i in lst:
b = int(i)
userNumbers.append(b)
numsum = sum(userNumbers)
return numsum
You'll notice that the other stuff is missing: we can add it back in, but it's unnecessary. You might want to use the following to test while developing:
def digit_sum(n):
lst = list(str(n))
userNumbers = []
for i in lst:
b = int(i)
userNumbers.append(b)
numsum = sum(userNumbers)
return numsum
print "digit_sum(%s) = %s" % (434, digit_sum(434))
print "digit_sum(%s) = %s" % (123, digit_sum(123))
print "digit_sum(%s) = %s" % (555, digit_sum(555))
print "digit_sum(%s) = %s" % (18, digit_sum(18))
print "digit_sum(%s) = %s" % (1001, digit_sum(1001))
When you run it, this yields:
digit_sum(434) = 11
digit_sum(123) = 6
digit_sum(555) = 15
digit_sum(18) = 9
digit_sum(1001) = 2
The good news is that these results are all correct, so you're off to a good start! One thing you'll learn quickly is that small, tedious details sometimes matter a lot in programming.
A small note
It's not always easy or obvious. So-called experts started at zero, and had to learn and make mistakes as well. Don't be discouraged! There are a lot of disparate concepts that you have to understand to know 100% what's happening. Nobody gets it all at once.
I respect that you're trying to learn and grow tremendously, as well as the fact that you're willing to ask questions. It's never easy, but that little burn that you might be getting in your brain when you try to understand all of this is what learning feels like. Embrace it! Ask questions!
Good luck with your learning!
Other resources
If I may, I recommend Zed Shaw's Learn Python the Hard Way as a
quality, free supplement to your learning journey. It's excellent!
I've also found that Udacity's Free Online Courses are a
magnificent resource for learning a lot of different things. Specifically, their "Intro"-level CS courses are in Python, and are well worth the time.
Peter Norvig's essay "Teach Yourself Programming in Ten Years" is considered a classic. It's going to be a long journey, enjoy it!
Again, good luck.
You have
n = userinput
while you need
n = userInput
Then it works.
As a followup to Ezra's excellent answer, I propose an implementation that uses the divmod builtin
def digit_sum(n):
s = 0
while 1:
n, d = divmod(n, 10)
s += d
if n == 0 : return s
Came up with the solution here, probably not the most elegant but this seems to work (thanks Ezra, gboffi, and user2357112 for pointing my initial oversights in the original code I posted, dumb mistakes..):
n = 1234
def digit_sum(n):
userNumbers = str(n)
numList = list(userNumbers)
dinksList = []
for i in numList:
new = int(i)
dinksList.append(new)
numSum = sum(dinksList)
return numSum
print digit_sum(n)