Generator function that always processes, but does not always yield - python-3.x

I have a generator function that does 2 things:
Reads a file and writes another file based on the output.
Yields key values for the record it's just written.
The issue is that I don't always want to do point (2), and when I call it in such a way that I only want the lines written to a new file, it simply doesn't get called (i.e. a print statement as the first line does not even get output. Try/except catches nothing here either when it's called).
I've set up a simplified test case just to verify that this is "normal", and it reproduces the same results.
test.py
from test2 import run_generator
if __name__ == '__main__':
print('*** Starting as generator ***')
for num in run_generator(max=10, generate=True):
print(f'Calling : {num}')
print('*** Starting without yielding ***')
run_generator(max=10, generate=False)
print('*** Finished ***')
test2.py
def run_generator(max, generate):
print('*** In the generator function ***')
sum = 1
for i in range(max):
print(f'Generator: {i}')
sum += i
if generate:
yield i
print(f'End of generator, sum={sum}')
This gives me the output:
$ python3 test.py
*** Starting as generator ***
*** In the generator function ***
Generator: 0
Calling : 0
Generator: 1
Calling : 1
Generator: 2
Calling : 2
Generator: 3
Calling : 3
Generator: 4
Calling : 4
Generator: 5
Calling : 5
Generator: 6
Calling : 6
Generator: 7
Calling : 7
Generator: 8
Calling : 8
Generator: 9
Calling : 9
End of generator, sum=46
*** Starting without yielding ***
*** Finished ***
In the test example, I'd like the generator function to still print the values when called but told not to yield. (In my real example, I still want it to do a f.write() to a different file, under which everything is nested since it's an with open(file, 'w') as f: statement.
Am I asking it to do something stupid? The alternative seems to be 2 definitions that do almost the same thing, which violates the DRY principle. Since in my primary example the yield is nested within the "with open", it's not really something that can be pulled out of there and done separately.

it simply doesn't get called - it's because you don't call it. To solve the problem call it like any other generator. For example, like you did in the first case: for num in run_generator(max=10, generate=False): pass.
I guess, another way is to use next(run_generator(max=10, generate=False)) syntax inside try/except since yield is never reached so you will get StopIteration error.
Or something like result = list(run_generator(5, True/False)).

Related

Python : can't pass "self" as the only argument

I'm writing a code to simplify a graph. In this case I've needed to remove a node of degree 2 and connect its two neighbors each other. Here is the simplified code
class node():
def __init__(self,ind):
#some code
self.neighbors=queue() #queue is another class defined by me
self.distances=queue()
#some code
def addngh(self,nd,distance):
#some code
def remngh(self,nd): #remove node "nd" from neighbors queue
#some code
def d2noderem(self): #removing self node from its left,right neighbors' "neighbors" queue by passing self to left and right's "remngh" function
left,right = self.neighbors[0:2]
#some code
left.remngh(self) #======= Error occurs at here ==========
right.remngh(self)
#some code
when I call that d2noderem function the following error occurs
File "/path/to/file/simplifygraphs.py", line 51, in d2noderem left.remngh(self)
TypeError: remngh() missing 1 required positional argument: 'nd'
Then I tried with
left.remngh(self,self)
and this is the result
File "/path/to/file/simplifygraphs.py", line 51, in d2noderem left.remngh(self,self)
TypeError: remngh() takes 2 positional arguments but 3 were given
I can't understand how did the no of args increased from 0 to 3 by adding 1 more argument.
And I couldn't find a solution for this problem yet.
How to overcome this type of problem?
I appreciate your help very much
The method 'remng' expects an argument as defined by the parameter 'nd' in def remngh(self,nd): Since you're calling it without supplying the expected argument, it's throwing an error.
You should either provide the expected argument or rewrite the function entirely.

Python code does not come out when first return statement is executed

enter image description hereI am a begineer in Python development and learning python on python 3.6
When I executed below code ,I expected it to terminate when first return statement is executed and I was expecting Output 4.
But It is iterating 3 times and giving output as 8.
As per my understanding as soon as return statement is executed it should come out of the function. Why this is not happening.
#!/bin/python3
def stockmax(prices):
# Write your code here
# Write your code here
count=0
profit=0
maximum=max(prices)
#print(maximum)
index_max=prices.index(maximum)
#print(index_max)
if len(prices)<=1:
return(profit)
else:
for i in range(len(prices)):
if i<index_max:
profit=profit-prices[i]
#print("profit if",profit)
count=count+1
#print("count is",count)
elif i==index_max:
#print(profit)
profit=profit+(prices[i]*count)
#print("profit elif",profit)
count=0
else:
profit=profit+stockmax(prices[i:])
return(profit) # should terminate on executing first return
x=(stockmax([5,4,3,4,5]))
print(x)
By calling stockmax inside of itself, you are opening up a new 'scope'. We can treat these as levels on a building. When you call it, you are essentially moving up a floor, or gaining a level. To get back to the ground floor, or the main 'scope' you need to go back through all of the lower floors. We can do this easily by using return a little bit sooner. In your code it would look a little like this:
def stockmax(prices):
count=0
profit=0
maximum=max(prices)
index_max=prices.index(maximum)
if len(prices)<=1:
return(profit)
else:
for i in range(len(prices)):
if i<index_max:
profit=profit-prices[i]
count=count+1
elif i==index_max:
profit=profit+(prices[i]*count)
count=0
else:
profit=profit+stockmax(prices[i:])
return(profit) # Use return here instead!
This would give us the desired output of 4.

How to replace the call to random.randint() in a function tested with pytest?

I'm new to programming and did search a lot through the questions but couldn't find an answer to my present problem.
I am writing a little game in python 3.8 and use pytest to run some basic tests.
One of my tests is about a function using random.randint()
Here's an extract of my code :
import random
...
def hit(self, enemy, attack):
dmg = random.randint(self.weapon.damage_min, self.weapon.damage_max) + self.strength // 4
hit() does other things after that but the problem is with this first line.
I tried to use monkeypatching to get a fake random number for the test :
def test_player_hit_missed(monkeypatch, monster, hero):
monkeypatch.setattr('random.randint', -3)
hero.hit(monster, 'Scream')
assert monster.life == 55
When I run the test, I get this error :
TypeError: 'int' object is not callable
From what I understand, the monkey-patch did replace randint() by the number I indicated (-3), but then my function hit() did try to call it nonetheless.
I thought -3 would replace randint()'s result.
Can someone explain me :
- why this doesn't work (I probably haven't correctly understood the behavior of the monkeypatch) ?
- and how I can replace the call to random.randint() by the value -3 during the test ?
Thanks

Tensorflow while loop runs only once

The below while loop should print "\n\nInside while..." 10 times but when I run the graph, "\n\nInside while..." is printed exactly once. Why is that?
i = tf.constant(0)
def condition(i):
return i < 10
def body(i):
print("\n\nInside while...", str(i))
return i + 1
r = tf.while_loop(condition, body, [i])
Your issue comes from conflating TensorFlow graph building with graph execution.
The functions you pass to tf.while_loop get executed once, to generate the TensorFlow graph responsible for executing the loop itself. So if you had put a tf.Print in there (for example, saying return tf.Print(i+1, [i+1])) you'd see it print 10 times when the loop is actually executed by the TensorFlow system.
I know practically nothing about TensorFlow and cannot help you with your immediate problem, but you can accomplish something similar (maybe) if you write your code differently. Following the logic of your program, a different implementation of while_loop was devised below. It takes your condition and body to run a while loop that has been parameterized with functions passed to it. Shown below is a conversation with the interpreter showing how this can be done.
>>> def while_loop(condition, body, local_data):
while condition(*local_data):
local_data = body(*local_data)
return local_data
>>> i = 0
>>> def condition(i):
return i < 10
>>> def body(i):
print('Inside while', i)
return i + 1,
>>> local_data = while_loop(condition, body, (i,))
Inside while 0
Inside while 1
Inside while 2
Inside while 3
Inside while 4
Inside while 5
Inside while 6
Inside while 7
Inside while 8
Inside while 9
>>> local_data
(10,)
>>>

Python error cannot delete function call

In python I tried calling a function in a function - when I finished it says "error cannot delete function call". Why is this happening? Inside the nested function there is a delete used, but that is for a different variable! I tried a few things like printing the function instead but got the same answer.
Here is the code which isn't working:
def BracketStart():
Round2Normal=[]
Round1Loser=[]
if len(List) % 2 == 0:
print("The Current Match Is %s VS %s" %(min(List),max(List)))
starting=input("Start Match? --> ")
if starting=="Yes":
Winner1=input("Who Won? --> ")
if Winner1==min(List):
Round2Normal.append(min(List))
Round1Loser.append(max(List))
del min(List)
del max(List)
min(List) is a function call -- so, yes, you are trying to delete a function call. Perhaps
List.remove(min(List))
is the sort of thing you want to do (though calling a list "List" isn't a good choice and removing elements from a list is a relatively expensive operation).

Resources