I have a function
def f():
while True:
blah
I want to alter f in such a way that the caller could control the number of times the while loop in f runs, without altering much of the code in f (specially not adding a counter in f). Something like
def f(num_executions = True):
while num_executions:
blah()
f() will run an infinite loop
but f(an_expression_that_evaluates_to_true_n_times) will run the while loop n times.
What could such an expression be?
UPDATE:
I know, there are plenty of way to control how many times a loop will run, but the real question here is -
Can an expression in python evaluate to True for configurable number of times?
Some ideas I am toying with
-making an expression out of list = list[:-1]
-modifying default parameters of a function within a function
No need for a while-loop. Use a for-loop:
>>> def f(n):
... for _ in range(n):
... dostuff()
_ is used as a variable name in a for loop normally to be a placeholder. This loop loops through n amount of times. So f(5) would loop five times.
While I agree with the others that this is a bad idea, it is entirely (and easily) possible:
class BoolChange:
def __init__(self):
self.count = 0
def __bool__(self):
self.count += 1
return self.count <= 5
x = BoolChange()
while x:
print("Running")
This outputs Running five times, then exits.
The main reason this is a bad idea is that it means checking the state of the object modifies it, which is weird behaviour people won't expect. I can't imagine a good use case for this.
You can't do exactly what you describe. What is passed in python is not an expression, but a value. An object. An Immutable object in general evaluate to either True or to False. It will not change during the loop. Mutable object can change its truth value, but you can't make arbitrary object change during a general loop (which does not touch it in any way). In general, as have been said here, you really need to use for statement, or pass in a callable object (say, a function):
def f(is_true = lambda x : True):
while is_true():
blah()
Note that the reason that the callable solution is acceptable, while the "hiding in boolean" #Lattyware demonstrated is not, is that the additional coputation here is explicit - the () tells the reader that almost anything can happen here, depending on the object passed, and you don't need to know that the __bool__ method in this object is silently called and is expected to have side effect.
def f(c=-1):
while c:
print 'blah'
if c > 0: c -= 1
How about using a generator as a coroutine?
def blah():
print 'hi'
def f():
while True:
blah()
yield
x = f()
next(x) # "hi"
The generator here isn't be used for what it yields, but you get to control how many times it blahs externally, because it yields control ever time it blahs.
for i in range(3):
next(x) # blah blah blah
This will also work -
def foo(n=[1,2,3]):
foo.func_defaults = tuple([foo.func_defaults[0][:-1]],)
return n
while foo():
blah()
Related
I'm a beginner to CS, and I've been trying to work through a Python book (Think Python) on my own.
I'm currently on recursion, but I'm a bit stuck.
The exercise asks for me to write a function called do_n that takes a function object and a number, n, as arguments, and that calls the given function n times.
This is my code:
def countdown(n):
if n<= 0:
print 'Blastoff'
return
else:
print n
countdown(n-1)
def do_n(f(n), x):
if x<=0:
return
else:
f(n)
do_n(f, x-1)
do_n(countdown(3), 3)
When I do this, there's an error because of invalid syntax in def do_n(f(n), x). If I change it to:
def do_n(f, x):
if x<=0:
return
else:
f(n)
do_n(f, x-1)
There is an error because n is not defined in the else statement.
How can I make this work?
You are almost there with your second example. It takes a function f and a max count x. n doesn't exist because you haven't written the loop to generate values for n yet. Turns out python has a builtin for that
def do_n(f, x):
for n in range(x):
f(n)
Now do_n takes a function object f and a count x, then calls the function count times. Notice the difference between f (the function object) and f(n) (the result of calling f with the value n). When you want to call do_n, you do it like this:
do_n(countdown, 3)
Not like
do_n(countdown(3), 3) # badness
That last one calls countdown and then calls do_n with its result.
def print_n(s,n):
if n<=0:
return
else:
print(s)
print_n(s,n-1)
def do_n(f,s,n,x):
if x<=0:
return
else:
f(s,n)
do_n(f,s,n,x-1)
do_n(print_n,'Hello',2,2)
It's a tricky one.
Basically, the solution is:
def do_n(f, n):
if n <= 0:
return
f(n)
do_n(f, n-1)
However, if you actually try combining it with print_n then all hell breaks loose.
First of all, we have to add one more argument, s, to the above code to be able to pass any string that we want to print out n times.
Second of all, the above do_n should be able to repeat any passed function n times so long as the function doesn't mess with do_n. Unfortunately, this is exactly what happens here.
The assignment from the book seems clear: "...write a function called do_n that takes a function object and a number, n, as arguments, and that calls the given function n times..." or in other words do_n calls any function passed to it n times. You'd think if we pass s = 'Hello' and n = 3 then do_n(f, s, n) should output 'Hello' 9 times... because print_n('Hello', 3) prints 'Hello' 3 times and do_n(print_n, 'Hello', 3) is supposed to triple that result... gotcha.
What actually happens is in the first instance of recursion do_n does indeed print out 'Hello' 3 times, because print_n says so. But in the next instance do_n prints out 'Hello' only two times, because it has subtracted 3-1 in the previous instance and now print_n('Hello', 2). In the last instance do_n prints 'Hello' only once, for the same reason. So the total amount of 'Hello' output is 6.
Finally, if you use FOR LOOP instead of recursion to define do_n, you will actually get the correct result of 9 outputs:
def print_n(s, n):
if n <= 0:
return
print(s)
print_n(s, n-1)
def do_n(f, s, n):
for i in range(n):
f(s, n)
do_n(print_n, 'Hello', 3)
P.S.
On Page 52 of Think Python 2E we are encouraged to use recursion over for loop:
"For simple examples like this, it is probably easier to use a for loop. But we will see examples later that are hard to write with a for loop and easy to write with recursion, so it is good to start early."
However, in this particular exercise, using recursion forces an output which clashes with the assignment, seeing that do_n and print_n are at odds with each other because constructing do_n with the help of recursion wrongly reduces n in each instance.
Did I get it right?
thinkpython2e
recursion
How do I create/call a python generator in Genshi? Is that even possible?
For example, (and no i'm not looking for an alternate solution to this problem, of which there are many, including enumerate on the for each, etc):
<?python
""" a bunch of other code ... """
def bg_color_gen():
""" Alternate background color every call """
while 1:
yield "#FFFFFF"
yield "#EBEBEB"
?>
And then calling this function:
<fo:block background-color="${bg_color_gen()}">First entry</fo:block>
<fo:block background-color="${bg_color_gen()}">Second entry</fo:block>
<fo:block background-color="${bg_color_gen()}">Third entry</fo:block>
This has nothing to do with my < fo:block >, which you could replace with < div >. It is not an FO question but a Genshi question.
I'm guessing Genshi doesn't recognize the 'yield' and runs 'while 1' ad-infinitum?
Also, I do realize I could use a global to keep track of a counter, and then call
counter++
if counter%yieldCount==0: return "#FFFFFFF"
elif counter%yieldCount==1: return "#EBEBEB"
But this is not a generator and gets ugly very quickly!
Clarification:
Another way to ask this question: how would you code
def fib():
a,b = 0,1
while True:
yield a
b = a+b
yield b
a = a+b
Which would then be called in the sentence "The first number is $fib(), the second is $fib(), the third is $fib(), and so on."
================================================
Updated full solution based on accepted answer:
<?python
def fib_generator():
a,b = 0,1
while True:
yield a
b = a+b
yield b
a = a+b
fib = fib_generator()
?>
The first number is ${next(fib)},
the second is ${next(fib)},
the third is ${next(fib)}, and so on.
Without knowing the structure of your content, I would suggest the following:
<fo:block py:for="i, entry in entries"
background-color="${'#FFFFFF' if i % 2 else '#EBEBEB'}">
${entry}
</fo:block>
However if you truly want to use a generator then you could just evaluate using Python's native next()
<py:with vars="color=bg_color_gen();">
<fo:block background-color="${next(color)}">
</py:with>
You would want to declare the generator first and then call next on it to get a yielded color.
In this case you are passing three different instances of the generator created by calling bg_color_gen() ie)
# this creates a generator
>>> bg_color_gen()
<generator object bg_color_gen at 0x02B21A30>
>>> bgcg = bg_color_gen()
# this gets values
>>> next(bgcg)
'#FFFFFF'
>>> next(bgcg)
'#EBEBEB'
>>> next(bgcg)
'#FFFFFF'
>>>
i'm trying to generate an endless stream of results given a function f and an initial value x
so first call should give the initial value, second call should give f(x), third call is f(x2) while x2 is the previous result of f(x) and so on..
what i have come up with:
def generate(f, x):
return itertools.repeat(lambda x: f(x))
which does not seem to work. any ideas? (i cant use yield in my code). also i cant use more than 1 line of code for this problem. any help would be appreciated.
also note that in a previous ex. i was asked to use the yield. with no problems:
while True:
yield x
x = f(x)
this works fine. but now.. no clue how to do it without
In Python 3.3, you can use itertools.accumulate:
import itertools
def generate(f, x):
return itertools.accumulate(itertools.repeat(x), lambda v,_:f(v))
for i, val in enumerate(generate(lambda x: 2*x, 3)):
print(val)
if i == 10:
break
I think this works:
import itertools as it
def g(f, x):
return it.chain([x],(setattr(g, 'x', f(getattr(g, 'x', x))) or getattr(g, 'x') for _ in it.count()))
def f(x):
return x + 1
gen = g(f, 1)
print next(gen)
print next(gen)
print next(gen)
print next(gen)
Of course, it relys on some sketchy behavior where I actually add an attribute to the function itself to keep the state. Basically, this function will only work the first time you call it. After that, all bets are off.
If we want to relax that restriction, we can use a temporary namespace. The problem is that to get a temporary namespace we need a unique class instance (or class, but an instance is cleaner and only requires 1 extra set of parenthesis). To make that happen in one line, we need to create a new function inline and use that as a default argument:
import itertools as it
def g(f, x):
return (lambda f, x, ns=type('foo', (object,), {})(): \
it.chain([x],
(setattr(ns, 'x', f(getattr(ns, 'x', x))) or getattr(ns, 'x')
for _ in it.count()))
)(f, x)
def f(x):
return x + 1
gen = g(f, 1)
print next(gen) == 1
print next(gen) == 2
print next(gen) == 3
print next(gen) == 4
print "first worked?"
gen2 = g(f, 2)
print next(gen2) == 2
print next(gen2) == 3
print next(gen2) == 4
I've broken it into a few lines, for readability, but it's a 1-liner at heart.
A version without any imports
(and the most robust one yet I believe).
def g(f, x):
return iter(lambda f=f, x=x, ns=type('foo', (object,), {'x':x}): ((getattr(ns, 'x'),setattr(ns, 'x', f(getattr(ns, 'x'))))[0]), object())
One trick here is the same as before. We create a lambda function with a mutable default argument to keep the state. Inside the function, we build a tuple. The first item is what we actually want, the second item is the return value of the setattr function which is used to update the state. In order to get rid of the itertools.chain, we set the initial value on the namespace to the value of x so the class is already initialzed to have the starting state. The second trick is that we use the two argument form of iter to get rid of it.count() which was only used to create an infinite iterable before. iter keeps calling the function you give it as the first argument until the return value is equal to the second argument. However, since my second argument is an instance of object, nothing returned from our function will ever be equal to it so we've effectively created an infinite iterable without itertools or yield! Come to think of it, I believe this last version is the most robust too. Previous versions had a bug where they relied on the truthfulness of the return value of f. I think they might have failed if f returned 0. This last version fixes that bug.
I'm guessing this is some sort of homework or assignment? As such, I'd say you should take a look at generator expressions. Though I agree with the other commenters that this seems an exercise of dubious value...
Python has an elegant way of automatically generating a counter variable in for loops: the enumerate function. This saves the need of initializing and incrementing a counter variable. Counter variables are also ugly because they are often useless once the loop is finished, yet their scope is not the scope of the loop, so they occupy the namespace without need (although I am not sure whether enumerate actually solves this).
My question is, whether there is a similar pythonic solution for while loops. enumerate won't work for while loops since enumerate returns an iterator. Ideally, the solution should be "pythonic" and not require function definitions.
For example:
x=0
c=0
while x<10:
x=int(raw_input())
print x,c
c+=1
In this case we would want to avoid initializing and incrementing c.
Clarification:
This can be done with an endless for loop with manual termination as some have suggested, but I am looking for a solution that makes the code clearer, and I don't think that solution makes the code clearer in this case.
Improvement (in readability, I'd say) to Ignacio's answer:
x = 0
for c in itertools.takewhile(lambda c: x < 10, itertools.count()):
x = int(raw_input())
print x, c
Advantages:
Only the while loop condition is in the loop header, not the side-effect raw_input.
The loop condition can depend on any condition that a normal while loop could. It's not necessary to "import" the variables referenced into the takewhile, as they are already visible in the lambda scope. Additionally it can depend on the count if you want, though not in this case.
Simplified: enumerate no longer appears at all.
Again with the itertools...
import itertools
for c, x in enumerate(
itertools.takewhile(lambda v: v < 10,
(int(raw_input()) for z in itertools.count())
)
):
print c, x
If you want zero initialization before the while loop, you can use a Singleton with a counter:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(
cls, *args, **kwargs)
cls.count=0
else:
cls.count+=1
return cls._instance
Then there will only be one instance of Singleton and each additional instance just adds one:
>>> Singleton().count # initial instance
0
>>> Singleton().count
1
>>> Singleton().count
2
>>> Singleton().count
3
Then your while loop becomes:
while Singleton():
x=int(raw_input('x: '))
if x>10: break
print 'While loop executed',Singleton().count,'times'
Entering 1,2,3,11 it prints:
x: 1
x: 2
x: 3
x: 11
While loop executed 4 times
If you do not mind a single line initialization before the while loop, you can just subclass an interator:
import collections
class WhileEnum(collections.Iterator):
def __init__(self,stop=None):
self.stop=stop
self.count=0
def next(self): # '__next__' on Py 3, 'next' on Py 2
if self.stop is not None:
self.remaining=self.stop-self.count
if self.count>=self.stop: return False
self.count+=1
return True
def __call__(self):
return self.next()
Then your while loop becomes:
enu=WhileEnum()
while enu():
i=int(raw_input('x: '))
if i>10: break
print enu.count
I think the second is the far better approach. You can have multiple enumerators and you can also set a limit on how many loops to go:
limited_enum=WhileEnum(5)
I don't think it's possible to do what you want in the exact way you want it. If I understand right, you want a while loop that increments a counter each time through, without actually exposing a visible counter outside the scope of the loop. I think the way to do this would be to rewrite your while loop as a nonterminating for loop, and check the end condition manually. For your example code:
import itertools
x = 0
for c in itertools.count():
x = int(raw_input())
print x, c
if x >= 10:
break
The problem is that fundamentally you're doing iteration, with the counter. If you don't want to expose that counter, it needs to come from the loop construct. Without defining a new function, you're stuck with a standard loop and an explicit check.
On the other hand, you could probably also define a generator for this. You'd still be iterating, but you could at least wrap the check up in the loop construct.
To make the process of recursion more visible, this example is given:
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
else:
return fib(n - 1) + fib(n - 2)
def trace(f):
f.indent = 0
def g(x):
print('| ' * f.indent + '|--', f.__name__, x)
f.indent += 1
value = f(x)
print('| ' * f.indent + '|--', 'return', repr(value))
f.indent -= 1
return value
return g
fib = trace(fib)
print(fib(4))
I can understand "what" the trace function does, but I don't understand "how". Specifically:
1) Why do we have f.indent instead of, say, simple indent = 0 (well, I see that that doesn't work, but I don't see why).
2) I don't understand how
print('| ' * f.indent + '|--', 'return', repr(value))
is not executed until a value is found.
Would somebody be kind enough to explain the whole thing thoroughly?
Whew. All right, here we go!
First, you have a function, any function. In your case, that's fib(). Now, in python, functions are also objects, and they can be created in runtime, so we can actually do this:
def give_me_a_function():
def f(x):
return x
return f
(Warning: horrible repetition of the word 'function' for the rest of this answer).
Well, we defined a function that takes no arguments and returns,... another function? That's right! Functions are objects! You can create them in runtime! So we defined a second function inside our original one, and returned it, as we would any other object.
Now, let's do something a tad more complicated:
def alter(other_function):
def altered(x):
return other_function(x) + 1
return altered
What the hell was that?
Well, we defined a function, alter(). Just as in the example above, it creates a function in run-time and returns it, as the object it is. That much we already covered.
Now, if functions are objects, and can be created and returned, why wouldn't you be able to pass one as argument? And call it, while it you're at it! That's right: alter() takes a function as argument(*), and uses it.
All it takes to have alter() is combining the above magic with this new magic: we receive a function as an argument, create another one on the fly that makes use of it, and return this new function-object!
Let's try it.
>>> def f(x):
... return 2*x
>>> new_function = alter(f)
>>> f(2)
4
>>> new_function(2)
5
There it goes! alter() takes my f(), creates a new function that will return f() + 1, and gives that to me as a return value. I assign it to new_function, and I have a new, home-brewed, run-time created function.
(I did warn you about the use of the word 'function', did I not?)
Now, to your piece of code. You're doing something more complicated than just f() + 1. Or not? Well, you're creating a new function that takes the original one, calls it, and prints some data. That's not much more magical than what we just did. Where's the big difference?
Well, there is one detail: fib() is recursive, so it calls itself, right? Nope! Not itself. It calls fib(), and you happened to do this:
fib = trace(fib)
WHAM. fib() is not itself anymore! Now fib() is trace(fib)! So when fib() goes into recursion, it's not calling itself, it's calling the wrapped version of itself we created.
That's why the indentation is handled like that. Look at trace() again, now knowing it's actually recursively indenting, and it makes sense, doesn't it? You want to have one indentation per level of recursion, so increment it, call fib() (which, remember, is now trace(fib)), and then when we're back (so the recursion went and came, and we're about to return to a previous step in the calling chain) we decrement it.
If you still don't see it, try moving all the functionality to fib(). Forget about the decorating function, that's plain confusing.
Ah. I really hope this helps, and that the 2.000 guys that beat me to the answer didn't already make this question obsolete.
Cheers!
(*) Yeah yeah duck typing yadda yadda callable objects bla bla irrelevant.
If we would just use indent instead of f.indent, it would become a local variable inside the inner function g(), due to the assignments to indent in g(). Since this seems to be Python 3, it is actually not necessary to use a function attribute -- you could also use the nonlocal keyword:
def trace(f):
indent = 0
def g(x):
nonlocal indent
print('| ' * indent + '|--', f.__name__, x)
indent += 1
value = f(x)
print('| ' * indent + '|--', 'return', repr(value))
indent -= 1
return value
return g
The second print() call won't be reached before at least one invocation of f() has returned. It appears in the code after the call to f(), so the flow of execution will only get there after f() has returned.
If you would store the indent level just in indent, it would be local to the current function call. Each time the function is called, you would get a new variable, with the value 0. By storing it in the function object, it will be the same for each function call (function are objects too in python).
For the second part, I'm not really sure what you are asking. Whenever the argument is greater then 1, two new calls to fib are made, and thus, no value is returned. Until the argument equals 1 or 0, a return call is made.