On 02/05/2013 10:18 AM, Ulrich Eckhardt wrote:
Hello Pythonistas!
Below you will find example code distilled from a set of unit tests,
usable with Python 2 or 3. I'm using a loop over a list of parameters to
generate tests with different permutations of parameters. Instead of
calling util() with values 0-4 as I would expect, each call uses the
same parameter 4. What I found out is that the name 'i' is resolved when
Foo.test_1 is called and not substituted inside the for-loop, which
finds the global 'i' left over from the loop. A simple "del i" after the
loop proved this and gave me an according error.
Now, I'm still not sure how to best solve this problem:
* Spell out all permutations is a no-go.
* Testing the different iterations inside a single test, is
inconvenient because I want to know which permutation exactly fails and
which others don't. Further, I want to be able to run just that one
because the tests take time.
* Further, I could generate local test() functions using the current
value of 'i' as default for a parameter, which is then used in the call
to self.util(), but that code is just as non-obviously-to-me correct as
the current code is non-obviously-to-me wrong. I'd prefer something more
stable.
Any other suggestions?
Thank you!
Uli
# example code
from __future__ import print_function
import unittest
class Foo(unittest.TestCase):
def util(self, param):
print('util({}, {})'.format(self, param))
for i in range(5):
def test(self):
self.util(param=i)
setattr(Foo, 'test_{}'.format(i), test)
unittest.main()
There is only one instance of i, so it's not clear what you expect.
Since it's not an argument to test(), it has to be found in the closure
to the function. In this case, that's the global namespace. So each
time the function is called, it fetches that global.
To put it another way, you're storing the same function object 5 times.
If you need to have separate function objects that already know a
value for i, you need to somehow bind the value into the function object.
One way to do it, as you say, is with default parameters. A function's
default parameters are each stored in the object, because they're
defined to be evaluated only once. That's sometimes considered a flaw,
such as when they're volatile, and subsequent calls to the function use
the same value. But in your case, it's a feature, as it provides a
standard place to store values as known at function definition time.
The other way to do it is with functions.partial(). I can't readily
write you sample code, as I haven't messed with it in the case of class
methods, but partial is generally a way to bind one or more values into
the actual object. I also think it's clearer than the default parameter
approach.
Notice that globals may be defined after a function that references
them, which is a way of cross-checking the logic you already discovered.
The names are only looked up when the function is actually called.
This same logic applies to nested functions; the class definition is an
unnecessary complication; of course I understand it's needed for unittest.
The main place where I see this type of problem is in a gui, where
you're defining a callback to be used by a series of widgets, but you
have a value that IS different for each item in the series. You write a
loop much like you did, and discover that the last loop value is the
only one used. The two cures above work, and you can also use lambda
creatively.
--
DaveA
--
http://mail.python.org/mailman/listinfo/python-list