Augmented Assignment - python

I am new to the world of Python and programming in general, and today I have faced a problem with augmented assignment. I unfortunately do not understand the code, and what for i in range(multiplier) and answer *= number does. I tried understanding it but I still don't really get the logic behind it. Can somebody please explain?
number = 5
multiplier = 8
answer = 0
for i in range(multiplier):
answer *= number
print(answer)

range([start], stop[, step])
range is a function that takes a number and return a list of number from 0 ... right through to the number you gave it as an argument.
BUT THE KEY TO NOTICE IS THAT IT WILL NEVER INCLUDE THE NUMBER YOU
TOLD IT TO COUNT TO
. Example :
This is an example of giving the function range 1 argument:
>>> # One parameter
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
Here is an example of giving it two arguments where the first argument is telling the function what to start the list it returns at. The second argument is where it should end:
>>> # Two parameters
>>> for i in range(3, 6):
... print(i)
...
3
4
5
Here is an even cooler example where we use the third argument as well. This argument tells the function to count from whatever number you told it to start at, right through to whatever number you told it to stop at (just like the above to examples)... only now, the third argument tells it in what steps to count:
Like count from 2 to 12 but count in 2's:
>>> # Three parameters
>>> for i in range(2, 12, 2):
... print(i)
...
2
4
6
8
10
SO....
the for loop is just iterating through that list of numbers that is given back by the function range
so lets break that for loop in to pseudo code.
***loop***
for i in range(multiplier):
answer *= number
***Pseudo code***
Give me a list of numbers
(not giving it a start value but only a value to end the count at).
The numbers has to be from 0 to multiplier (8).
so range(multiplier) -> range(8) -> [0, 1, 2, 3, 4, 5, 6, 7]
now you have a list
now you ask the compiler to go through that list.
you say : go through that list and everytime you at a new number, give it to me in the for of a variable called i.
then when you have i, you don't use it because i was just trying to loop 8 (multiplier) times... but now take the answer and add to it this (answer * number) ... this will happen 8 times because you loop 8 times

Related

Creating a new method

I'm working on a MOOC on Python Programming and am having a hard time finding a solution to a problem set. I hope you can provide some assistance.
The problem is:
The Fibonacci sequence is a number sequence where each number is the sum of the previous two numbers. The first two numbers are defined as 0 and 1, so the third number is
1 (0 + 1 = 1), the fourth number is 2 (1 + 1 = 2), the fifth number is 3 (1 + 2 = 3), the sixth number is 5(2 + 3 = 5), and so on.
Below we've started a class called FibSeq. At any time, FibSeq holds two values from the Fibonacci sequence: back1 and back2.
Create a new method inside FibSeq called next_number. The next_number method should:
Calculate and return the next number in the sequence,
based on the previous 2.
Update back2 with the former value of back1, and update
back1 with the new next item in the sequence.
This means that consecutive calls to next_number should yield each consecutive number from the Fibonacci sequence. Calling next_number 5 times would print 1, 2, 3, 5, and 8.
My code is below:
class FibSeq:
def __init__(self):
self.back1 = 1
self.back2 = 0
def next_number(self):
self.back1 = self.back1 + self.back2
self.back2 = self.back1 - self.back2
yield(self.back1)
f = FibSeq()
for i in range(5):
s = f.next_number()
print(next(s))
My code returns the following:
1
2
3
5
8
<generator object FibSeq.next_number at 0x7f68f6fbe678>
<generator object FibSeq.next_number at 0x7f68f6fbe678>
<generator object FibSeq.next_number at 0x7f68f6fbe678>
<generator object FibSeq.next_number at 0x7f68f6fbe678>
<generator object FibSeq.next_number at 0x7f68f6fbe678>
However, it should only return 1,2,3,5,8, after running the below code:
newFib = FibSeq()
print(newFib.next_number())
print(newFib.next_number())
print(newFib.next_number())
print(newFib.next_number())
print(newFib.next_number())
Why does my code return the last 5 "error" statements like this <generator object FibSeq.next_number at 0x7f68f6fbe678>?
Thank you.
Although I agree that a generator is a much more pythonic solution to this problem, I don't believe the assignment wants you to write a generator. What it says is:
This means that consecutive calls to next_number should yield each consecutive number from the Fibonacci sequence.
Their use of the word "yield" (rather than the more natural "return") is confusing, to be sure, but "return" is surely what they meant because each call to a generator function returns an iterator, not a value, and you need to call next on the returned iterator to get successive values.
Generators and class instances are two different ways of solving the same problem, which is how to retain state while producing successive values of a sequence. In the object-oriented solution, you create an object by calling the class' constructor, and then you repeatedly call an instance method (such as next_number. That would lead to a solution like this:
class FibSeq:
def __init__(self):
self.back1, self.back2 = 1, 0
def next_number(self):
self.back1, self.back2 = self.back1 + self.back2, self.back1
return self.back1
# Example usage:
fibber1 = FibSeq()
for i in range(1, 6):
print(i, fibber1.next_number())
That prints
1 1
2 2
3 3
4 5
5 8
(Note that Fib(0) is also 1. To my mind, a Fibonacci generator should start at 0, but to conform to the expectation that the first five calls to next_number, I started the counter at 1.)
If we want another sequence starting at the beginning, we just create another instance of the class. We can then use both instances independently. Here I print out the next five values produced by the first object, whose next value produced will be Fib(6), and in another column, I output the first five values produced by a new object:
fibber2 = FibSeq()
for i in range(6, 11):
print(i, fibber2.next_number(), fibber1.next_number())
That prints
6 1 13
7 2 21
8 3 34
9 5 55
10 8 89
And it's easy to see that the two instances of FibSeq are each using their own state (that is, the members back1 and back2).
But that's not very Pythonic (in my opinion). In Python, we can create a generator function to do the same thing; the generator's state is now contained in local variables, not so easily available for inspection. Calling the generator function returns an iterator --a different one each time, with its own local variables-- and the iterators can be used in a for statement or a comprehension. Generators are distinguished from ordinary functions by the fact that they include at least one yield statement. They normally don't include a return statement, or if they do, it doesn't return anything. This has nothing to do with the return value of calling the generator; as I said, calling the generator returns an iterator, but that's done automatically as soon as you call the generator, before any statement has been executed.
So here's the generator version of the Fibonacci sequence:
def FibGen():
back1, back2 = 1, 0
while True:
back1, back2 = back1 + back2, back1
yield back1
Now, we can use a simple for loop, but with a bit of caution because the iterator produced by FibGen never stops. To specify the desired number of values generated, I can zip it with a range iterator:
for i, f in zip(range(1, 6), FibGen()):
print(i, f)
# Output:
1 1
2 2
3 3
4 5
5 8
This works because zip stops iterating as soon as one of its argument iterators is done.
In that for loop, I created the iterator without saving it. But I might want to save it, in order to do something like the side-by-side display from the O-O example. That works in a very similar fashion:
# Create one iterator and print the first five values
fibber1 = FibGen()
for i, f in zip(range(1, 6), fibber1):
print(i, f)
# Now create another, independent iterator:
fibber2 = FibGen()
for i, f1, f2 in zip(range(6, 11), fibber1, fibber2):
print(i, f1, f2)
The output from the two loops:
1 1
2 2
3 3
4 5
5 8
6 1 13
7 2 21
8 3 34
9 5 55
10 8 89
I can also use the generator to fill in a list comprehension, although again I need to be careful to stop the generation, since I have enough memory to store an infinite list. Again, zipping with a range is a simple way to control the generation count:
v = [f for i,f in zip(range(10), FibGen())]
print(v)
# Output:
[1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Collecting generated values is obviously simpler for iterators which only generate a finite number of values. But on the whole, you should avoid collecting generated values. It's surprising how rarely you actually need to save the list.

Query on [Python sum of number in array, ignoring sections of specific numbers]

With reference to Kelly's Short O(n) solution at
Python sum of number in array, ignoring sections of specific numbers
Can someone explain to me why the numbers between 6 and 9 are not included in the sum? I have tried deriving the answer myself using:
def summer_70(lst):
it = iter(lst)
return sum(x for x in it
if x != 4 not in it)
print(summer_70([4, 5, 6, 7, 8, 9])) #Output 5 not 35 ??
But I still do not understand how the code executes.
Thanks in advance :D
I'll take the code from Kelly's answer as your's lacks the or operator.
Generate iterator from the given list
it = iter(lst)
For each entity in the given iterator, start summing it.
return sum(x for x in it
if x != 6 or 9 not in it)
If the current value we are iterating not equals 6, the or condition already return True and it adds it to the sum.
Once we find x == 6, we go into the other condition
9 not in it
Currently, the iterator contains only the remaining values we didn't sum.
This condition will iterate the rest of the values until it finds a 9.
If it didn't found a 9, then we summed only the values until the first 6.
If it finds a 9, the iterator will point on the next value after the 9 (this way we skipped the values between 6 to 9), and we'll do the same if condition again (sum if it's not 6, or check again until we find a 9)
Given
lst = [1,2,6,7,8,9,10]
When we reach the 6, we already summed 1, 2.
Then, the or condition will search for 9 in the remaining list:
[7,8,9,10]
Found 9, and the remaining list:
[10]
Sum 10 and we'll get
1+2+10 = 13
Link to the accepted answer - https://stackoverflow.com/a/68975493/3125312
The code in question
def summer_69(lst):
it = iter(lst)
return sum(x for x in it
if x != 6 or 9 not in it)
Lets work through this with an example.
Say we have a list of numbers
[1,2,6,7,9,5]
and we need calculate the sum skipping any region starting with a 6 and ending with a 9. The sum would be 8.
We can start by trying to solve it using a simpler but longer code
set a counter total to 0 - this will be the default sum
set a flag should_skip to let us know if we need to skip a number or not
iterate through the list and add all numbers but only if should_skip is False
Now the most important question arises - how do we know if we should skip a number? Checking whether a number is a 6 or 9 is not enough as we need a skip a whole section which may include other numbers as well. This is where we make use of the should_skip flag. And we need to check only 2 conditions.
If we hit a 6 in our iteration we know this is the start of the section to skip. so we set should_skip to True
If we hit a 9 in out iteration then this is the end of the section to skip. we can set should_skip to False.
We can add to the total if should_skip is False
Converting this into code looks like
input = [1, 2, 6, 7, 9, 5]
total = 0 # The default sum
should_skip = False
for number in input:
if number == 6:
# start of the section to skip, keep skipping till we reach a 9
should_skip = True
if not should_skip:
# add it to the total!
total += number
if number == 9:
# End of section to skip, we can start adding numbers to the total
should_skip = False
print(total) # 8
If you can understand the above logic then it will be extremely easy to understand the shorter implementation. Lets get back to it and start analyzing.
There are 2 necessary things that both codes are doing. "iterating" through the code and "summming" the numbers that are not in a section to skip.
We wrote the code for it ourselves but Python has inbuilt function for it as well called iter and sum
iter is a function that returns an iterator object. whats special about iterators is that they are memory efficient and that they remove an element when proceeding to the next one.
e.g
if our iterator contains iter(3,6,4)
after one iteration the contents look like iter(6,4)
and after the second iteration there is only one element remaining i.e. iter(4)
sum as the name suggests returns the sum. the default is 0
def summer_69(lst):
it = iter(lst) # turn our input into an iterator object
return sum(x for x in it # add it to the total!
# but wait we need to check if we are in a section to skip first
if x != 6 # number is not a 6,
or 9 not in it # there are no 9s remaining in the iterator so its safe to add
)
Step by Step breakdown:
1st iteration -> x is 1 and `it` is `[1,2,6,7,9,5]`
2nd iteration -> x is 2 and `it` is `[2,6,7,9,5]`
3rd iteration -> x is 6 and `it` is `[6,7,9,5]`
4th iteration -> x is 7 and `it` is `[7,9,5]`
5th iteration -> x is 9 and `it` is `[9,5]`
6th iteration -> x is 5 and `it` is `[5]`
At each iteration the condition being checked is x != 6 or 9 not in it? this is how we know that we are in a section to skip or not.

Python 3: Executing a For loop x number of times by using a variable

Edit: Fixed the screwed up brackets, sorry. Should have caught this. Let's see if this clarifies what I'm trying to do.
I want to do a basic program that takes the variable "run" and runs the following code that number of times by using a For loop.
run = 3
def program(run):
for i in range(5):
print("The number is",i)
program(run)
What I want is to get is:
the number is 0
the number is 1
the number is 2
the number is 3
the number is 4
the number is 0
the number is 1
the number is 2
the number is 3
the number is 4
the number is 0
the number is 1
the number is 2
the number is 3
the number is 4
i.e. the "program" loops however many times I set "run" to equal.
Let's break the code and the error message down a bit:
for i in range[5]:
TypeError: 'type' object is not subscriptable
"Subscripting" is the way you access a particular element in a container, like a list or a dict. For example, to get the first (0th) element of a list called foo, you'd say foo[0], and to get the value associated with the key "bar" in a dictionary called foo, you'd say foo["bar"].
Note that when you just use the [] symbols by themselves (instead of putting them after some other identifier), they serve a different purpose: they construct a list. The expression [5] is a list that contains a single element (the number 5). To represent more elements, you comma-separate them, e.g. [0, 1, 2, 3, 4].
So what the error is telling you is that a type object can't be subscripted. Does that mean that range is a type? Yes! In Python, a range is a type of iterable object that represents a range of numbers. On its own, the word range in a Python program refers to the entire class of range objects (that is, a type of object), not a particular range object.
You construct a range the same way you construct most objects, by calling the type as a constructor function, with parameters in parentheses. For example, to construct a range with 5 numbers in it (0 to 4, similar to the list [0, 1, 2, 3, 4]) you can simply say range(5).
So to print all the numbers from 0 to 4 you can do:
for i in range(5):
print("The number is", i)
In your code, the run variable serves no purpose and can be removed completely.
Functions can take any number of parameters, or none at all, and since in this case your function doesn't do anything with the parameter you pass in, it can be simply:
def program():
for i in range(5):
print("The number is", i)
program()
If you wanted to make the length of the range variable, that would make sense as a parameter -- you just need to pass the parameter along to your range construction:
def program(num):
for i in range(num):
print("The number is", i)
runs = 3
for _ in range(runs):
program(5)
You'd use range, but you would use parenthesis to call the function instead of braces. I.e. range(5) instead of range[5]. Or you could use your run variable like range(run)
In short you are seeing the error because you are using square brackets and not parentheses range() is a function. Change it to
def program(run):
for i in range(5):
print("The number is",i)
program(run)
The number is 0
The number is 1
The number is 2
The number is 3
The number is 4
and it works fine.
If you want to the program to loop the same number of times as specified in run then you can do this:
run = 5
def program(run):
for i in range(run):
print("the number is",i)
program(run)
def program(n):
for i in range(0,n+1):
print("The number is",i)
program(5)
ouput
The number is 0
The number is 1
The number is 2
The number is 3
The number is 4
The number is 5
Give this a try. Hopefully understood the assignment:
run = 3
def program(run):
nums = [f'The number is: {n}' for r in range(run) for n in range(5)]
print('\n'.join(nums))
program(run)
This uses list comprehension to loop twice which translates to:
run = 3
def program(run):
for r in range(run):
for n in range(5):
print(f'The number is: {n}')
program(run)
Or:
run = 3
def program():
for n in range(5):
print(f'The number is: {n}')
for r in range(run):
program()
Output:
The number is: 0
The number is: 1
The number is: 2
The number is: 3
The number is: 4
The number is: 0
The number is: 1
The number is: 2
The number is: 3
The number is: 4
The number is: 0
The number is: 1
The number is: 2
The number is: 3
The number is: 4
Whelp, I finally figured it out - the reason I couldn't seem to find a solution anywhere to "could this be done and how would I go about it" is that in Python at least, a For statement cannot do this kind of loop, only a While can.
So my experiment of trying to do this via a For is a moot point, it's not something that can be coded without using While.
Thanks for the help.

Python - Random sample from a range whilst avoiding certain values

I have been reading up about the random.sample() function in the random module and have not seen anything that solves my problem.
I know that using random.sample(range(1,100),5) would give me 5 unique samples from the 'population'...
I would like to get a random number in range(0,999). I could use random.sample(range(0,999),1) but why then am I thinking about using random.sample() ?
I need the random number in that range to not match any number in a separate array (Say, [443,122,738])
Is there a relatively easy way I could go about doing this?
Also, I am pretty new to python and am definitely a beginner -- If you would like me to update the question with any information I may have missed then I will.
EDIT:
Accidentally said random.range() once. Whoops.
One way you can accomplish that is by simply checking the number and then appending it to a list where you can then use the numbers.
import random
non_match = [443, 122, 738]
match = []
while len(match) < 6: # Where 6 can be replaced with how many numbers you want minus 1
x = random.sample(range(0,999),1)
if x not in non_match:
match.append(x)
There are two main ways:
import random
def method1(lower, upper, exclude):
choices = set(range(lower, upper + 1)) - set(exclude)
return random.choice(list(choices))
def method2(lower, upper, exclude):
exclude = set(exclude)
while True:
val = random.randint(lower, upper)
if val not in exclude:
return val
Example usage:
for method in method1, method2:
for i in range(10):
print(method(1, 5, [2, 4]))
print('----')
Output:
1
1
5
3
1
1
3
5
5
1
----
5
3
5
1
5
3
5
3
1
3
----
The first is better for a smaller range or a larger list exclude (so the choices list won't be too big), the second is better for the opposite (so it doesn't loop too many times looking for an appropriate option).

Control the index of a Python for loop

How do you control the index of a python for loop? (or can you? or should you?)
For Example:
for i in range(10):
print i
i = i + 1
Yields:
0
1
2
3
4
5
6
7
8
9
I want it to yield:
0
2
3
4
5
6
7
8
9
10
I really do apologize if I'm just completely off track with this question, and my brain is completely failing me at the moment and the solution is obvious.
Why am I asking?
This is irrelevant to the question, but relevant to the why I need the answer.
In a Python script I'm writing, I am doing something like this:
for i in persons:
for j in persons[-1(len(persons) - i - 1:]:
if j.name in i.name:
#remove j.name
else:
#remove i.name
#For every person (i), iterate trough every other person (j) after person (i)
#The reason I ask this question is because sometimes I will remove person i.
#When that happens, the index still increases and jumps over the person after i
#So I want to decrement the index so I don't skip over that person.
Maybe I am going about this completely the wrong way, maybe I should use a while loop and control my indices.
How do you control the index of a python for loop? (or can you? or should you?)
You can't / shouldn't - the loop control variable will be reassigned at the end of each iteration to the next element of whatever it is you are iterating over (so that i = i + 1 has no effect, since i will be reassigned to something different for the next iteration anyway). If you want to control the index like that, you should use a while-loop:
i = 0
while i < 10:
print i
i = i + 1
Although, Python's range function is more flexible than you might realize. For instance, to iterate in steps of 2 you can simply use something like
for i in range(0, 10, 2):
print i
Check out the docs on range here, or from the docstr:
range([start,] stop[, step]) -> list of integers
Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3]. The end point is omitted!
These are exactly the valid indices for a list of 4 elements.
To get range from 0-10, just do:
> for i in range(0, 11):
> print i
> 0
> 1
> 2
> 3
> 4
> 5
> 6
> 7
> 8
> 9
> 10
By the way, it's pointless to do the i = i + 1, cause every iteration in the for loop will change i again. Whatever you set it to in the loop, will get overwritten every time the loop starts over.

Categories