Re: [Twisted-Python] Counting errors in a DeferredList and avoiding Unhandled error in Deferred messages
Hi Jean-Paul You can do this (if you replace `pass´ with `None´, anyway) or you can pass `consumeErrors=True´ to the `DeferredList´ initializer which will make it do something equivalent. Thanks. Sorry - I should have just read the docs again :-) Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Global variables in modules...
Peter == Peter Otten [EMAIL PROTECTED] writes: Peter [EMAIL PROTECTED] wrote: [snip] ...can someone explain why invoking a.py prints 0? I would have thought that the global variable 'g' of module 'a' would be set to 1... Peter When you run a.py as a script it is put into the sys.modules module Peter cache under the key __main__ instead of a. Thus, when you import Peter a the cache lookup fails and a.py is executed again. You end up with Peter two distinct copies of the script and its globals [snip] Suggesting the following horribleness in a.py g = 0 def main(): global g import b g = 1 b.callb() if __name__ == __main__: import sys, __main__ sys.modules['a'] = __main__ main() Terry -- http://mail.python.org/mailman/listinfo/python-list
Question on using distutils.core.run_setup
I'm trying to programmatically install something built using distutils. I found distutils.core.run_setup and can use it via dist = run_setup('setup.py', ['-q', 'install']) Is that the recommended way to do an install from inside Python (as opposed to doing it on the command line)? If so, how can I find where the thing(s) I installed now resides? I saw dist.packages but that just has top-level package names. I could __import__ these (and then use module.__file__), but that's not a good solution as it may run code I don't want run. On my machine, I can see the packages have been installed under the system's python2.5/site-packages directory. But how can I determine that programmatically? I don't see anything useful on the distutils.dist.Distribution instance I'm getting back from run_setup. Thanks! Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: European python developers
Hi Jerry I am hoping one or two members of this list might help me locate in Europe Do you mean you want to re-locate here or that you're trying to locate (i.e., find) people already here? My personal first choice is Spain I'm in Barcelona (but not looking for work!). There are a couple of companies around that I know of who use Python and quite a lot of individual programmers. The point is that I can't really afford to run around and experiment, so I was hoping for some helpful comments or suggestions on how to research I'm not sure what you're wanting to research. See first comment above. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: flattening a dict
Hi Arnaud Benjamin Here's a version that's a bit more general. It handles keys whose values are empty dicts (assigning None to the value in the result), and also dict keys that are not strings (see the test data below). It's also less recursive as it only calls itself on values that are dicts. But it returns a dict whose keys are tuples. You then need to decide what to do with this. Hence the helper function strdictflatten for the case when all dict keys can be converted to str. As I told Arnaud in email, I greatly prefer his version for its elegance. Terry def dictflatten(d, prefix=None): result = {} if prefix is None: prefix = tuple() for k, v in d.iteritems(): key = prefix + (k,) if isinstance(v, dict): if v: result.update(dictflatten(v, key)) else: result[key] = None else: result[key] = v return result def strdictflatten(d, sep='/'): return dict((sep.join(map(str, k)), v) for k, v in dictflatten(d).iteritems()) if __name__ == '__main__': test = { : {}, 4 : 5, mays : {eggs : spam}, jam : {soda : {love : dump}}, lamba : 23 } d = dictflatten(test) print strdictflatten(test) {'': None, 'lamba': 23, 'mays/eggs': 'spam', '4': 5, 'jam/soda/love': 'dump'} -- http://mail.python.org/mailman/listinfo/python-list
Re: flattening a dict
Arnaud == Arnaud Delobelle [EMAIL PROTECTED] writes: Arnaud BTW, I keep using the idiom itertools.chain(*iterable). I guess Arnaud that during function calls *iterable gets expanded to a tuple. Arnaud Wouldn't it be nice to have an equivalent one-argument function Arnaud that takes an iterable of iterables and return the 'flattened' Arnaud iterable? This reminds me of a function I wrote a while back that iterates over all its arguments, even calling passed functions and iterating over their results. I knew *even less* about Python then than I do now; please set flamethrowers to Constructive Criticism. http://www.fluidinfo.com/terry/2007/05/07/iteranything/ Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: type, object hierarchy?
Arnaud == Arnaud Delobelle [EMAIL PROTECTED] writes: Arnaud Are you suggesting a new axiom for propositional logic: Arnaud ((P = Q) ^ (R = Q)) = (P = R) ? Arnaud I.e. in fruit logic: every orange is a fruit and every apple is a Arnaud fruit, therefore every orange is an apple. This is otherwise known as Winterson's law: 1. Every orange is a fruit 2. Every apple is a fruit = Oranges are not the only fruit Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
My output looks better sorted. I.e., for s in sorted(solutions)... Giving the easier to read/compare: Target 234, numbers = (100, 9, 7, 6, 3, 1) (6, 1, 'add', 100, 'mul', 7, 'sub', 9, 'add', 3, 'div') (6, 9, 'sub', 7, 'mul', 1, 'sub', 100, 'add', 3, 'mul') (7, 3, 'mul', 6, 'sub', 9, 'mul', 1, 'sub', 100, 'add') (7, 6, 'mul', 3, 'mul', 1, 'sub', 100, 'add', 9, 'add') (7, 6, 'mul', 3, 'mul', 100, 'sub', 9, 'mul', 1, 'mul') (100, 1, 'sub', 3, 'div', 7, 'mul', 6, 'sub', 9, 'add') (100, 1, 'sub', 7, 'mul', 9, 'sub', 3, 'div', 6, 'add') (100, 7, 'mul', 3, 'mul', 6, 'add', 9, 'div', 1, 'mul') * (100, 7, 'mul', 6, 'sub', 1, 'sub', 9, 'add', 3, 'div') (100, 7, 'sub', 3, 'div', 1, 'sub', 9, 'add', 6, 'mul') (100, 7, 'sub', 3, 'div', 6, 'sub', 1, 'add', 9, 'mul') (100, 9, 'add', 7, 'add', 1, 'add', 3, 'div', 6, 'mul') (100, 9, 'sub', 7, 'div', 6, 'mul', 3, 'mul', 1, 'mul') (100, 9, 'sub', 7, 'sub', 6, 'sub', 3, 'mul', 1, 'mul') Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Text-based data inspector for Python?
kj == kj [EMAIL PROTECTED] writes: kj I've only recently started programming in Python, trying to wean kj myself from Perl. One of the things I *really* miss from Perl is kj a 100% mouse-free data inspector, affectionally known as the Perl kj debugger, PerlDB, or just perl -d. With it I can examine the most kj elaborate data structures with ease: You actually liked the perl debugger... gasp! OK, I used it too, but it left a few things to be desired (the mouse was not one). kj And, since it's text-based, I can run it within a shell in Emacs, and kj transfer anything I want between it and an editing buffer without even kj a THOUGHT of touching the filthy mouse! If there's a greater joy in kj life I have yet to find it. I use M-x pydb to debug python from inside emacs. I like it more than the straight pdb as it's a bit more like gdb. In pydb (and pdb) there's p and pp to print and pretty print a python object. They work pretty well there's no need for the mouse. kj NOTE: In my address everything before the first period is backwards; kj and the last period, and everything after it, should be discarded. Nice. See http://www.fluidinfo.com/terry/2007/10/31/stagnant-email-address-arms-race/ Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
Hi Arnaud and Dan Arnaud == Arnaud Delobelle [EMAIL PROTECTED] writes: What was wrong with the very fast(?) code you sent earlier? Arnaud I thought it was a bit convoluted, wanted to try something I Arnaud thought had more potential. I think the problem with the second Arnaud one is that I repeat the same 'fold' too many times. and later: Arnaud Yes, I've been doing this by writing an 'action' (see my code) that Arnaud takes note of all reached results. These are both comments about pruning, if I understand you. In the first you weren't pruning enough and in the second you're planning to prune more. I'm giving up thinking about this problem because I've realized that the pruning solution is fundamentally subjective. I.e., whether or not you consider two solutions to be the same depends on how hard you're willing to think about them (and argue that they're the same), or how smart you are. I have a new version that does some things nicely, but in trying to do efficient pruning, I've realized that you can't satisfy everyone. Some examples for the problem with target 253, numbers = 100, 9, 7, 6, 3, 1 Firstly, there are nice solutions that go way negative, like: 1 7 6 3 9 sub mul mul sub or 1 - 7 * 6 * (3 - 9) Here's a pruning example. Are these the same? 1 3 7 100 9 sub sub mul sub or 1 - 3 * (7 - 100 - 9) 1 3 7 9 100 sub add mul sub or 1 - 3 * (7 - 9 - 100) I think many people would argue that that's really the same and that one of the two should not appear in the output. The same goes for your earlier example for 406. It's 4 * 100 + 2 x 3, and 2 x 3 + 100 * 4, and so on. My latest program does all these prunings. But you can argue that you should push even further into eliminating things that are the same. You could probably make a pretty fast program that stores globally all the states it has passed through (what's on the stack, what numbers are yet to be used, what's the proposed op) and never does them again. But if you push that, you'll wind up saying that any two solutions that look like this: ... 1 add e.g. 6 9 3 sub mul 7 mul 1 add or 6 * (9 - 3) * 7 + 1 7 6 mul 9 3 sub mul 1 add or 7 * 6 * (9 - 3) + 1 are the same. And if we go that far, then a really smart person might argue that this 100 7 sub 9 sub 3 mul 1 add or (100 - 7 - 9) * 3 + 1 is also the same (because all these solutions get to 252 and then add 1, so they're clearly the same, right?): Once you've gone that far, you might even argue that on a particular problem of a particular difficulty (subjectively matching what your brain is capable of) all solutions that start out by pushing 1 onto the stack are actually the same. And if you're even smarter than that, you might just look at any problem and say Hey, that's easy! The answer is X or There's clearly no solution because you'd immediately just see the answer (if any) and that all others were just obvious variants. E.g., if I said to you: make 20 out of (20, 10, 10, 3), I imagine you could immediately list the answer(s?) 20 = 20 20 = 10 + 10 20 = 20 + 10 - 10 20 = 20 - 10 + 10 etc., and explain that those are all really the same. You'd prune on the spot, and consider it obvious that the pruning was fully justified. But my 6-year-old son couldn't tell you that, and I doubt he'd agree with your prunings. OK, enough examples. I'm just trying to illustrate that the (difficult) problem of efficiently pruning doesn't have an objective solution. Pruning is subjective. OTOH, the decision problem does this puzzle have any solutions or not (and if so, produce one) does. That's why I'm going to stop working on this. My stack solution code is fun, but working on making it prune better is a black hole. And to make matters worse, the more I push it (i.e., the more advanced its prunings get), the less likely (some) people are to agree that its output is still correct. You can't win. If anyone wants the stack simulation code, send me an email. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
Arnaud == Arnaud Delobelle [EMAIL PROTECTED] writes: Arnaud FWIW, I have a clear idea of what the space of solutions is, and Arnaud which solutions I consider to be equivalent. I'll explain it Arnaud below. I'm not saying it's the right model, but it's the one Arnaud within which I'm thinking. OK. This reinforces why I'm not going to work on it anymore, the solution is subjective (once you start pruning). Arnaud I think it's best to forbid negatives. Solutions always be written Arnaud without any negative intermediate answer. E.g. 1-7*6*(3-9) can be Arnaud done as 1+7*6*(9-3). That's a good optimization, and I think it's easy to prove that it's correct supposing the target is positive and the inputs are all positive. If you consider the intermediate results in a solution, then if you ever go negative it's because of an operation X (must be a sub or a div) and when you next become positive it's due to an operation Y (must be add or mul). So you can reflect that part of the computation by doing the opposite operations for that formerly sub-zero intermediate sequence. Arnaud I don't consider these to be equivalent, because their equivalence Arnaud depends on understanding the meaning of subtraction and addition. Ha - you can't have it both ways Arnaud! You don't want the computation to go negative... doesn't that (and my proof) have something to do with the inverse nature of add and sub? :-) Arnaud (I've also applied the 'big then small' rule explained below) And now you're taking advantage of your knowledge of and ... My original code did this big then small canonicalization too. That's my point exactly - pruning depends on who you are, how smart you are, how hard you work, your personal taste, etc. Arnaud I see a solution as a full ordered binary tree. Each node has a Arnaud weight (which is the result of the calculation it represents) and Arnaud the left child of a node has to be at least as heavy as the right Arnaud child. Each parent node can be labeled + - * or /. If a node x Arnaud has two children y and z and is labeled op, let me write x = (y Arnaud op z) Where does the sequence of numbers enter into this? You have a tree of operations - is it acting on a stack? What's on the stack? It sounds similar to what I've done. I walk up and down the tree, keeping the stack and the stack history, doing operations (including pushing onto the stack) and undoing them. There are several more prunings I could be doing, but these require looking further back in the stack. E.g., I force div before mul and sub before add, and I also apply the big then small rule to the intermediate stack results if there are series of identical operations (not just to a single operation). E.g. X / Y / Z can be re-ordered so that Y = Z, and A + B + C can be reordered so A = B = C. Doing it on the stack results is different (but similar) to doing it on the raw input numbers. There are lots of other little and more complex things you can do to prune. You want to prune early, of course. The stack model of computation make this hard because it's always legitimate to push all the numbers onto the stack, by which point you're already deep in the tree. And this approach only lets you do local pruning - i.e., that affect the branch of the tree you're on. If you stored the state of the stack, the operation you're about to do, and the (sorted) numbers remaining to be input, then if you ever hit that configuration elsewhere in the massive tree, you could know that you'd been that way before. But are you justified in pruning at that point? The identical state of the computation could have been arrived at via a very different method. But that's where the big speed-up in this approach is. At this realization I decided to give up :-) Arnaud To be perfectly honest (and expose my approach a little to your Arnaud argument) I added a three additional rules: Arnaud * Don't allow x - x Arnaud * Don't allow x * 1 Arnaud * Don't allow x / 1 Yes, I do these too, including not allowing a zero intermediate (which is a useless calculation that simply could not have been done - see, I have deep knowledge of zero!). Arnaud If there are repeats in the list of starting numbers, I don't worry Arnaud about repeating solutions. I handle most of those cases, but I didn't push all the way. With target 32 and input 8, 8, 8, 8 my code still gives 2 answers: 8 8 add 8 8 add add 8 8 add 8 add 8 add If anyone wants the stack simulation code, send me an email. Arnaud I'd like to see it :) I'll send it. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
Arnaud == Arnaud Delobelle [EMAIL PROTECTED] writes: Ha - you can't have it both ways Arnaud! You don't want the computation to go negative... doesn't that (and my proof) have something to do with the inverse nature of add and sub? :-) Arnaud I think I can have it both ways, here's why: the big then small Arnaud and no negatives rules are applied indiscriminately by the Arnaud algorithm: it doesn't need to know about the history of operations Arnaud in order to make a decision depending on their nature. OTOH, Arnaud identifying (a + b) - c and a + (b - c) for example, requires Arnaud inspection of the stack/tree/ calculation history. It is an Arnaud *informed* decision made by the algorithm But this is having it both ways too :-) The reason the algorithm can do it indiscriminately is because *you* know that it always applies, and *you* figure out and implement the algorithm that way. The algorithm doesn't have a mind of its own, after all. BTW, there are some very important issues here. One is about representation (thinking about representation is one of my pet vices). When you try to solve some real-world problem with a computer, you must choose a representation. What many people seem to fail to recognize is that in so doing you've already begun to solve the problem. If you pick a better representation, your algorithm can potentially be much dumber and still be hugely faster than a brilliant algorithm on a terrible representation. In maintaining an A B invariant in operations A + B and A * B, you're using insight into the problem to influence representation. With that better representation, your algorithm doesn't have to do so much work. I wrote some more about this a while ago at http://www.fluidinfo.com/terry/2007/03/19/why-data-information-representation-is-the-key-to-the-coming-semantic-web/ Arnaud Note that disallowing 0 and disallowing x - x are equivalent, as Arnaud the only way to get your first 0 is by doing x - x. That depends. I allow negative numbers in the input, and also 0 (I just don't let you use it :-)) Arnaud Thanks for the discussion, it's made me realise more clearly what I Arnaud was doing. Me too! Thanks. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
Hi Arnaud I've tried a completely different approach, that I imagine as 'folding'. I thought it would improve performance over my previous effort but extremely limited and crude benchmarking seems to indicate disappointingly comparable performance... I wrote a stack-based version yesterday and it's also slow. It keeps track of the stack computation and allows you to backtrack. I'll post it sometime, but it's too slow for my liking and I need to put in some more optimizations. I'm trying not to think about this problem. What was wrong with the very fast(?) code you sent earlier? Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
Arnaud == Arnaud Delobelle [EMAIL PROTECTED] writes: Arnaud In countdown you are not required to use all numbers to reach the Arnaud target. This means you are missing solutions, e.g. (1, 3, 6, Arnaud 'mul', 'add', 7 , 'add', 9, 'mul') Hi Arnaud. Thanks, I didn't know that. The fix is a simple change to the first if my function below. WRT to the missing solution, note that my code only allowed multiplication by 1 if it was the last thing done. That was because you can multiply by 1 at any time, and I didn't want to see those trivially equivalent solutions (same goes for adding 0). Seeing as you're allowed to omit numbers, I've now gotten rid of those trivial operations altogether in my solution. Code and output below. Terry from operator import * def countdown(target, nums, numsAvail, value, partialSolution, solutions, ops=(add, mul, sub, div)): if value == target or not any(numsAvail): # Ran out of available numbers. Add the solution, if we're right. if value == target: solutions.add(tuple(partialSolution)) elif value is None: # Use each distinct number as a starting value. used = set() for i, num in enumerate(nums): if num not in used: numsAvail[i] = False used.add(num) partialSolution.append(num) countdown(target, nums, numsAvail, num, partialSolution, solutions, ops) numsAvail[i] = True partialSolution.pop() else: for op in ops: for i, num in enumerate(nums): if numsAvail[i]: numsAvail[i] = False moreAvail = any(numsAvail) try: lastNum, lastOp = partialSolution[-2:] except ValueError: lastNum, lastOp = partialSolution[-1], None # Don't allow any of: if not any(( # Div: after mul, by 1, by 0, producing a fraction. (op == div and (lastOp == 'mul' or num = 1 or value % num != 0)), # If initial mul/add, canonicalize to 2nd operator biggest. ((op == mul or op == add) and lastOp is None and num lastNum), # Don't allow add or sub of 0. ((op == add or op == sub) and num == 0), # Don't allow mult by 1. (op == mul and num == 1), # Don't allow sub after add (allow add after sub). (op == sub and lastOp == 'add'), # If same op twice in a row, canonicalize operand order. (lastOp == op.__name__ and num lastNum) )): partialSolution.extend([num, op.__name__]) countdown(target, nums, numsAvail, op(value, num), partialSolution, solutions, ops) del partialSolution[-2:] numsAvail[i] = True for nums, target in (((100, 9, 7, 6, 3, 1), 253), ((100, 9, 7, 6, 3, 1), 234), ((2, 3, 5), 21), ((7, 8, 50, 8, 1, 3), 923), ((8, 8), 16), ((8, 8, 8), 8), ((8, 0), 8), ((7,), 8), ((), 8), ((8, 8, 8, 8), 32)): solutions = set() countdown(target, nums, [True,] * len(nums), value=None, partialSolution=[], solutions=solutions) print %d solutions to: target %d, numbers = %s % (len(solutions), target, nums) for s in sorted(solutions, cmp=lambda a, b: cmp(len(a), len(b)) or cmp(a, b)): print '\t', s $ time countdown.py 8 solutions to: target 253, numbers = (100, 9, 7, 6, 3, 1) (6, 3, 'mul', 1, 'sub', 9, 'mul', 100, 'add') (7, 6, 'mul', 9, 'add', 3, 'mul', 100, 'add') (9, 3, 'sub', 7, 'mul', 6, 'mul', 1, 'add') (100, 9, 'sub', 7, 'sub', 3, 'mul', 1, 'add') (3, 1, 'add', 6, 'mul', 7, 'sub', 9, 'mul', 100, 'add') (7, 1, 'add', 6, 'mul', 3, 'mul', 100, 'add', 9, 'add') (7, 6, 'add', 3, 'add', 1, 'add', 9, 'mul', 100, 'add') (100, 7, 'sub', 6, 'sub', 3, 'mul', 9, 'sub', 1, 'add') 19 solutions to: target 234, numbers = (100, 9, 7, 6, 3, 1) (6, 3, 'mul', 7, 'add', 1, 'add', 9, 'mul') (7, 1, 'add', 9, 'mul', 6, 'add', 3, 'mul') (7, 3, 'mul', 1, 'sub', 6, 'add', 9, 'mul') (7, 6, 'mul', 3, 'mul', 100, 'sub', 9, 'mul') (100, 1, 'sub', 3, 'div', 7, 'sub', 9, 'mul') (100, 1, 'sub', 7, 'mul', 9, 'add', 3, 'div') (100, 7, 'mul', 3, 'mul', 6, 'add', 9, 'div') (100, 9, 'sub', 7, 'div', 6, 'mul', 3, 'mul') (100, 9, 'sub', 7, 'sub', 6, 'sub', 3, 'mul') (6, 1, 'add', 100, 'mul', 7, 'sub', 9, 'add', 3, 'div') (6, 9,
Re: Just for fun: Countdown numbers game solver
Paul == Paul Rubin http://phr.cx@NOSPAM.invalid writes: Hi Paul Paul Here's my latest, which I think is exhaustive, but it is very slow. Paul It prints a progress message now and then just to give the user some Paul sign of life. It should print a total of 256-8 = 248 of those Paul messages and it takes around 20 minutes on my old 1 Ghz Pentium 3. Paul It does find plenty of solutions for 234, e.g. Paul 234 mul(9,sub(mul(mul(6,mul(3,1)),7),100)) Paul There are some obvious clunky ways to speed it up through memoizing Paul and symmetry folding but I can't help thinking there's a concise Paul elegant way to do that too. The symmetry folding is important, you can cut off many recursive calls. My code runs in 0.5 seconds for the 234 problem, but I have a MacBook Pro :-) Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
dg == dg google groups [EMAIL PROTECTED] writes: dg It's great how many different sorts of solutions (or almost solutions) dg this puzzle has generated. Speedwise, for reference my solution posted dg above takes about 40 seconds on my 1.8GHz laptop, and the less elegant dg version (on my webpage linked to in the original post) takes about 15 dg seconds. It seems to me like surely this problem can be more dg efficiently solved than that? dg Paul: 758 = 8+(5*((2+4)*25)) For this I get: 2 solutions to: target 758, numbers = (2, 4, 5, 8, 25) (4, 2, 'add', 25, 'mul', 5, 'mul', 8, 'add') (25, 4, 'mul', 5, 'sub', 8, 'mul', 2, 'sub') real0m0.118s user0m0.074s sys 0m0.044s Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
cokofreedom == cokofreedom [EMAIL PROTECTED] writes: cokofreedom Terry, your technique is efficient and pretty readable! All cokofreedom that could be added now is a way to output the data in a more cokofreedom user-friendly print. Yes, and a fix for the bug Arnaud just pointed out :-) Below is code to print things more nicely for you. Terry from operator import * from itertools import izip def countdown(target, nums, numsAvail, value, partialSolution, solutions, ops=(add, mul, sub, div)): if value == target or not any(numsAvail): # Add the solution, if we're right. if value == target: solutions.add(tuple(partialSolution)) elif value is None: # Use each distinct number as a starting value. used = set() for i, num in enumerate(nums): if num not in used: numsAvail[i] = False used.add(num) partialSolution.append(num) countdown(target, nums, numsAvail, num, partialSolution, solutions, ops) numsAvail[i] = True partialSolution.pop() else: for op in ops: for i, num in enumerate(nums): if numsAvail[i]: numsAvail[i] = False moreAvail = any(numsAvail) try: lastNum, lastOp = partialSolution[-2:] except ValueError: lastNum, lastOp = partialSolution[-1], None # Don't allow any of: if not ( # Div: after mul, by 1, by 0, producing a fraction. (op == div and (lastOp == 'mul' or num = 1 or value % num != 0)) or # If initial mul/add, canonicalize to 2nd operator biggest. ((op == mul or op == add) and lastOp is None and num lastNum) or # Don't allow add or sub of 0. ((op == add or op == sub) and num == 0) or # Don't allow mult by 1. (op == mul and num == 1) or # Don't allow sub after add (allow add after sub). (op == sub and lastOp == 'add') or # If same op twice in a row, canonicalize operand order. (lastOp == op.__name__ and num lastNum) ): partialSolution.extend([num, op.__name__]) countdown(target, nums, numsAvail, op(value, num), partialSolution, solutions, ops) del partialSolution[-2:] numsAvail[i] = True op2sym = { 'add' : '+', 'sub' : '-', 'mul' : '*', 'div': '/', } def pretty(s): out = [str(s[0])] lastOp = None for value, op in izip(*(iter(s[1:]),) * 2): if (op == 'mul' or op == 'div') and (lastOp == 'add' or lastOp == 'sub'): out.insert(0, '(') out.append(')') out.append(op2sym[op]) out.append(str(value)) lastOp = op return ''.join(out) for nums, target in ( ((100, 9, 7, 6, 3, 1), 253), ((100, 9, 7, 6, 3, 1), 234), ((2, 3, 5), 21), ((7, 8, 50, 8, 1, 3), 923), ((8, 8), 16), ((8, 8, 8), 8), ((8, 0), 8), ((7,), 8), ((), 8), ((8, 8, 8, 8), 32), ((2, 4, 5, 8, 25), 758), ((2, 3, 4, 100), 406), ): solutions = set() countdown(target, nums, [True,] * len(nums), value=None, partialSolution=[], solutions=solutions) print %d solutions to: target %d, numbers = %s % (len(solutions), target, nums) for s in sorted(solutions, cmp=lambda a, b: cmp(len(a), len(b)) or cmp(a, b)): print '\t', pretty(s) Sample output: 8 solutions to: target 253, numbers = (100, 9, 7, 6, 3, 1) (6*3-1)*9+100 (7*6+9)*3+100 (9-3)*7*6+1 (100-9-7)*3+1 ((3+1)*6-7)*9+100 (7+1)*6*3+100+9 (7+6+3+1)*9+100 (100-7-6)*3-9+1 19 solutions to: target 234, numbers = (100, 9, 7, 6, 3, 1) (6*3+7+1)*9 ((7+1)*9+6)*3 (7*3-1+6)*9 (7*6*3-100)*9 ((100-1)/3-7)*9 ((100-1)*7+9)/3 (100*7*3+6)/9 (100-9)/7*6*3 (100-9-7-6)*3 ((6+1)*100-7+9)/3 ((6-9)*7-1+100)*3 (7*3-6)*9-1+100 7*6*3-1+100+9 (100-1)/3*7-6+9 ((100-1)*7-9)/3+6 (100*7-6-1+9)/3 ((100-7)/3-1+9)*6 ((100-7)/3-6+1)*9 (100+9+7+1)/3*6 -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
Arnaud == Arnaud Delobelle [EMAIL PROTECTED] writes: Arnaud Sorry I gave an incorrect example to illustrate my question last Arnaud night (I blame this on baby-induced sleep deprivation ;), so I'll Arnaud have another go: Arnaud Say I have 2, 3, 4, 100 and I want to make 406. AFAICS there is only Arnaud one way: (2*3)+(4*100), i.e. in postfix notation: Arnaud 2 3 * 4 100 * + Arnaud It seemed to me that your function wouldn't generate that sort of Arnaud solution (as you always extend partialSolution by [num, op] making Arnaud the subsequence [mul, add] impossible). Am I wrong? No, you're right. I actually started out with a stack solution, and then switched to something simpler (too simple). I'm going to redo it, but maybe not right now... Thanks! Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Just for fun: Countdown numbers game solver
Here's a solution that doesn't use any copying of lists in for recursion. It also eliminates a bunch of trivially equivalent solutions. The countdown function is 37 lines of non-comment code. Sample (RPN) output below. Terry from operator import * def countdown(target, nums, numsAvail, value, partialSolution, solutions, ops=(add, mul, sub, div)): if not any(numsAvail): # Ran out of available numbers. Add the solution, if we're right. if value == target: solutions.add(tuple(partialSolution)) elif value is None: # Use each distinct number as a starting value. used = set() for i, num in enumerate(nums): if num not in used: numsAvail[i] = False used.add(num) partialSolution.append(num) countdown(target, nums, numsAvail, num, partialSolution, solutions, ops) numsAvail[i] = True partialSolution.pop() else: for op in ops: for i, num in enumerate(nums): if numsAvail[i]: numsAvail[i] = False moreAvail = any(numsAvail) try: lastNum, lastOp = partialSolution[-2:] except ValueError: lastNum, lastOp = partialSolution[-1], None # Don't allow any of: if not any(( # Div: after mul, by 1, by 0, producing a fraction. (op == div and (lastOp == 'mul' or num = 1 or value % num != 0)), # If initial mul/add, canonicalize to 2nd operator biggest. ((op == mul or op == add) and lastOp is None and num lastNum), # Don't all subtracting 0 (instead allow adding 0). (op == sub and num == 0), # Don't allow adding 0 unless it's the very last thing we do. (op == add and num == 0 and moreAvail), # Don't allow mult by 1 unless it's the very last thing we do. (op == mul and num == 1 and moreAvail), # Don't allow sub after add (allow add after sub). (op == sub and lastOp == 'add'), # If same op twice in a row, canonicalize operand order. (lastOp == op.__name__ and num lastNum) )): partialSolution.extend([num, op.__name__]) countdown(target, nums, numsAvail, op(value, num), partialSolution, solutions, ops) del partialSolution[-2:] numsAvail[i] = True for nums, target in (((100, 9, 7, 6, 3, 1), 253), ((100, 9, 7, 6, 3, 1), 234), ((2, 3, 5), 21), ((7, 8, 50, 8, 1, 3), 923), ((8, 8), 16), ((8, 8, 8), 8), ((8, 0), 8), ((7,), 8), ((), 8), ((8, 8, 8, 8), 32)): print Target %d, numbers = %s % (target, nums) solutions = set() countdown(target, nums, [True,] * len(nums), value=None, partialSolution=[], solutions=solutions) for s in solutions: print '\t', s This produces: Target 253, numbers = (100, 9, 7, 6, 3, 1) (7, 6, 'add', 3, 'add', 1, 'add', 9, 'mul', 100, 'add') (7, 6, 'mul', 9, 'add', 3, 'mul', 100, 'add', 1, 'mul') (3, 1, 'add', 6, 'mul', 7, 'sub', 9, 'mul', 100, 'add') (100, 7, 'sub', 6, 'sub', 3, 'mul', 9, 'sub', 1, 'add') (7, 1, 'add', 6, 'mul', 3, 'mul', 100, 'add', 9, 'add') Target 234, numbers = (100, 9, 7, 6, 3, 1) (6, 1, 'add', 100, 'mul', 7, 'sub', 9, 'add', 3, 'div') (100, 1, 'sub', 3, 'div', 7, 'mul', 6, 'sub', 9, 'add') (7, 6, 'mul', 3, 'mul', 100, 'sub', 9, 'mul', 1, 'mul') (100, 7, 'sub', 3, 'div', 6, 'sub', 1, 'add', 9, 'mul') (7, 6, 'mul', 3, 'mul', 1, 'sub', 100, 'add', 9, 'add') (6, 9, 'sub', 7, 'mul', 1, 'sub', 100, 'add', 3, 'mul') (100, 9, 'sub', 7, 'sub', 6, 'sub', 3, 'mul', 1, 'mul') (100, 7, 'mul', 3, 'mul', 6, 'add', 9, 'div', 1, 'mul') (100, 1, 'sub', 7, 'mul', 9, 'sub', 3, 'div', 6, 'add') (100, 7, 'sub', 3, 'div', 1, 'sub', 9, 'add', 6, 'mul') (7, 3, 'mul', 6, 'sub', 9, 'mul', 1, 'sub', 100, 'add') (100, 7, 'mul', 6, 'sub', 1, 'sub', 9, 'add', 3, 'div') (100, 9, 'add', 7, 'add', 1, 'add', 3, 'div', 6, 'mul') (100, 9, 'sub', 7, 'div', 6, 'mul', 3, 'mul', 1, 'mul') Target 21, numbers = (2, 3, 5) (5, 2, 'add', 3, 'mul') Target 923, numbers = (7, 8, 50, 8, 1, 3) (50, 8, 'mul', 1, 'sub', 3, 'div', 7, 'mul', 8, 'sub') Target 16, numbers = (8, 8) (8, 8, 'add') Target 8, numbers = (8, 8, 8) (8, 8, 'sub', 8,
Re: writing Python in Emacs
Richard == Richard Szopa [EMAIL PROTECTED] writes: Richard I am a devoted Emacs user and I write a lot in Python. Me too. Richard I need the following features: Richard 1) Tab completion, ideally Slime like. That is, when there's not Richard enough letters to unambiguously complete a symbol, I want it to Richard show a buffer (w/o taking the focus) w/ the possible Richard completions. In an ideal world, it would be able to complete Richard fo.baTAB to foo.bar. I imagine this would require quite tight Richard Emacs-Python integration. I know this is not what you want, but I use hippie expand (M-/) to cycle through possible completions. It's not Python aware, but it is of some use. Richard 2) Sending the toplevel definition (class or function) to the Python Richard buffer. I switched to IPython to have better interaction with a spawned Python. To use IPython you need to use the Python mode that is NOT the one from (endorsed?) by the FSF. It gives you some completion (at least in the *Python* buffer) and you can send pieces of the buffer to the python process, via py-send-region (C-c |), py-execute-def-or-class (M-C-x), etc. Richard 3) Hints on function/method arguments. IDLE has this done nearly Richard right, but the hints are a bit too intrusive for me. I would like to Richard see them in the minibuffer. I don't have this. Richard 4) (optional) I would like to see the definition of a function Richard function or class by hitting M-. on its name. (I understand that Richard this may be impossible for methods, as Emacs would have to Richard automagically infer the type of the object). This is just an emacs tag file need. Have you googled for something like emacs tags python? The issue of methods might be overcome by just moving through tags with the same name. Yes, that requires _you_ to know when you've hit the right thing. That's not optimal, but it's better than nothing. Ideally you could send the definition to IPython, ask it for the class info, and use that to jump to the right tag. Richard I have tried a couple of times both python-modes (the one shipped w/ Richard Python and the one shipped w/ Emacs), pymacs and stuff like that... Richard And, as I said, never got it right. But, maybe I just cannot find the Richard way to configure it, and some configuration hints will be enough... If you have the time, please summarize your findings. The emacs/python world has always seemed quite amorphous to me too. Terry -- http://mail.python.org/mailman/listinfo/python-list
RE: Some Berkeley DB questions (being maintained? queries?)
Brian == Brian Smith [EMAIL PROTECTED] writes: Brian [EMAIL PROTECTED] wrote: 2. Are there good python libraries for bdb available, that are being maintained? Brian I would like to know the answer to this question too--if you have Brian used the pybsddb/bsddb.db module, please share your experience. I used it a little in mid-2006. I ran into one issue that I resolved by looking at the C source and fiddling with the Python interface. I sent mail to the people mentioned in the bsddb README file. One (Barry Warsaw) answered to say he hadn't looked at the code in a couple of years and was probably not the person to contact. 4. What are good, stable alternatives? From memory, the Chandler project also has an interface to BSD DB, but I don't remember any details at all. I'm also interested in any ongoing or planned work on the Python interface. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Open a List of Files
Fredrik == Fredrik Lundh [EMAIL PROTECTED] writes: Fredrik Seriously, for a limited number of files, the dictionary approach Fredrik is mostly pointless; you end up replacing Fredrik foo = open(foo) Fredrik foo.write(...) Fredrik with Fredrik somedict[foo] = open(foo) Fredrik somedict[foo].write(...) Yes, in some cases. But where you want to do multiple things you can always pull the file descriptor of of the dict into a local var. - It has less risk of error (much less repetition). Fredrik No, it hasn't. There's more to type when using the files, and you Fredrik have *two* things you can misspell; that is, you're replacing a Fredrik NameError with either a NameError or a Key Error. Well I meant less error risk in the context of opening the files. And I'm happy to get a NameError or KeyError if I make the mistake you describe. But bad code like this: messages = open(os.path.join(host_path,'messages.txt'), 'wb') deliveries = open(os.path.join(host_path,'deliveries.txt'), 'wb') actions = open(os.path.join(host_path,'deliveries.txt'), 'wb') doesn't give an error at all, and with all that identical and semi-identical code it's much less obvious where the error is. Cut paste errors like this are pretty common, and they can be quite hard to find (when they don't result in exceptions), in my experience. - It allows your code to later take a string file tag and write to that file by looking up its file descriptor in the dict. Fredrik Instead of allowing your code to take a file object and write to that Fredrik file by writing to that file object? No, see below: - You can close all open files with a trivial loop. Fredrik Ok, this is actually an advantage. Not that you need to do that very Fredrik often, since Python does it for you. Yes. That's partly why I said it would make him a better programmer in general, not just in Python. Fredrik And if you find yourself needing to do this a lot, you can of Fredrik course stuff all the output files in a list but *still* use the Fredrik variables to refer to the file when writing to them. Yes, as above. Fredrik There is one case where a dictionary of files makes perfect sense, of Fredrik course, and that's when you can associate the file with some *other* Fredrik value that you're *already* using. (Say, a user name or a machine name Fredrik or a severity level or something like that.) Yes. That's what I meant by my original - badly worded - remark (see above) that you could take a string file tag and use it to look up its file descriptor. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Open a List of Files
BJ == BJ Swope [EMAIL PROTECTED] writes: I (at least) think the code looks much nicer. BJ #Referring to files to write in various places... BJ open_files['deliveries'].write(flat_line) BJ open_files['deliveries'].write('\n') If you were doing a lot with the deliveries file at some point, you could put the file descriptor into a local variable deliveries = open_files['deliveries'] deliveries.write(flat_line) deliveries.write('\n') BJ #And finally to close the opened files BJ for fn in open_files.keys(): BJ open_files[fn].close() You don't need for fn in open_files.keys():, you can just use for fn in open_files:, but simpler than that is to just use the dictionary values: for fn in open_files.values(): fn.close() Regards, Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Open a List of Files
Hi BJ Fredrik Lundh [EMAIL PROTECTED] writes: Or in a dict: open_files = {} for fn in ['messages', 'recipients', 'viruses']: open_files[fn] = open(getfilename(fn), 'w') I decided that I was just trying to be too smooth by 1/2 so I fell back to ... messages = open(os.path.join(host_path,'messages.txt'), 'wb') deliveries = open(os.path.join(host_path,'deliveries.txt'), 'wb') actions = open(os.path.join(host_path,'actions.txt'), 'wb') parts = open(os.path.join(host_path,'parts.txt'), 'wb') recipients = open(os.path.join(host_path,'recipients.txt'), 'wb') viruses = open(os.path.join(host_path,'viruses.txt'), 'wb') esp_scores = open(os.path.join(host_path,'esp_scores.txt'), 'wb') I think you should revisit this decision. Something like Fredrik's code is the way to go. It has multiple advantages: - It's much shorter. - It's arguably easier to add/remove to/from. - It has less risk of error (much less repetition). - It allows your code to later take a string file tag and write to that file by looking up its file descriptor in the dict. - You can close all open files with a trivial loop. Also, if you start writing code like Fredrik's instead of like what you fell back on you'll make yourself a better programmer (in general, not just in Python). Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Two candies
Raymond == Raymond Hettinger [EMAIL PROTECTED] writes: Raymond * in most apps (except for sparse arrays), the initialization time Raymond for an array is dominated by the time spent actually doing Raymond something useful with the array (iow, this is an odd place to be Raymond optimizing) This brings to mind an old algorithms chestnut (exercise 2.12 in the 1st edition of Aho, Hopcroft Ullman [1]): If the implementation of an algorithm uses (for simplicity's sake) a square array to represent its data, why are _all_ such algorithms not necessarily O(n^2) due simply to the initialization requirement (supposing a model of computation that counts assignments)? Terry [1] http://www.amazon.com/Analysis-Algorithms-Addison-Wesley-Information-Processing/dp/0201000296 -- http://mail.python.org/mailman/listinfo/python-list
Re: getting n items at a time from a generator
Hi Igor Igor == Igor V Rafienko [EMAIL PROTECTED] writes: Also consider this solution from O'Reilly's Python Cookbook (2nd Ed.) p705 def chop(iterable, length=2): return izip(*(iter(iterable),) * length) Igor Is this *always* guaranteed by the language to work? Should the Igor iterator returned by izip() change the implementation and evaluate Igor the underlying iterators in, say, reverse order, the solution would Igor no longer function, would it? Or is it something the return value of Igor izip() would never do? Igor (I am just trying to understand the solution, not criticize it. Took Igor a while to parse the argument(s) to izip in the example). I had to look at it a bit too. I actually deleted the comment I wrote about it in my own code before posting it here and decided to simply say consider in the above instead :-) As far as I understand it, you're right. The docstring for izip doesn't guarantee that it will pull items from the passed iterables in any order. So an alternate implementation of izip might produce other results. If it did them in reverse order you'd get each n-chunk reversed, etc. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: getting n items at a time from a generator
Kugutsumen == Kugutsumen [EMAIL PROTECTED] writes: Kugutsumen On Dec 27, 7:07 pm, Paul Hankin [EMAIL PROTECTED] wrote: On Dec 27, 11:34 am, Kugutsumen [EMAIL PROTECTED] wrote: I am relatively new the python language and I am afraid to be missing some clever construct or built-in way equivalent to my 'chunk' generator below. Kugutsumen Thanks, I am going to take a look at itertools. I prefer the Kugutsumen list version since I need to buffer that chunk in memory at Kugutsumen this point. Also consider this solution from O'Reilly's Python Cookbook (2nd Ed.) p705 def chop(iterable, length=2): return izip(*(iter(iterable),) * length) Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Taxicab Numbers
rgalgon == rgalgon [EMAIL PROTECTED] writes: rgalgon I'm new to Python and have been putting my mind to learning it rgalgon over my holiday break. I've been looking over the functional rgalgon programming aspects of Python and I'm stuck trying to come up with rgalgon some concise code to find Taxicab numbers rgalgon (http://mathworld.wolfram.com/ TaxicabNumber.html). rgalgon Taxicab(n), is defined as the smallest number that can be rgalgon expressed as a sum of two positive cubes in n distinct ways rgalgon In Haskell something like this could easily be done with: rgalgon cube x = x * x * x rgalgon taxicab n = [(cube a + cube b, (a, b), (c, d)) rgalgon | a - [1..n], rgalgon b - [(a+1)..n], rgalgon c - [(a+1)..n], rgalgon d - [(c+1)..n], rgalgon (cube a + cube b) == (cube c + cube d)] rgalgon I'm looking for a succinct way of doing this in Python. I've been rgalgon toying around with filter(),map(), and reduce() but haven't gotten rgalgon anything half-way decent yet. rgalgon So, how would you implement this taxicab(n) function in Python? To give a fairly literal translation of your Haskell, you could just use this: def cube(n): return n ** 3 def taxicab(n): n += 1 return [(cube(a) + cube(b), (a, b), (c, d)) for a in xrange(1, n) for b in xrange(a + 1, n) for c in xrange(a + 1, n) for d in xrange(c + 1, n) if cube(a) + cube(b) == cube(c) + cube(d)] If you print the return value of this you get the same results as your Haskell examples. I added the following to my Python file: if __name__ == '__main__': import sys print taxicab(int(sys.argv[1])) Then I see $ time taxicab.py 20 [(1729, (1, 12), (9, 10)), (4104, (2, 16), (9, 15))] real0m0.098s user0m0.046s sys 0m0.046s Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: How to generate pdf file from an html page??
Grant == Grant Edwards [EMAIL PROTECTED] writes: Grant On 2007-12-19, abhishek [EMAIL PROTECTED] wrote: Hi everyone, I am trying to generate a PDF printable format file from an html page. Is there a way to do this using python. If yes then which library and functions are required and if no then reasons why it cant be done. Here's one way: --html2pdf.py- #!/usr/bin/python import os,sys inputFilename,outputFilename = sys.argv[1:3] os.system(w3m -dump %s | a2ps -B --borders=no | ps2pdf - %s % (inputFilename,outputFilename)) Note that this is highly insecure. outputFilename could be passed e.g., as /tmp/file.pdf; rm -fr /home/abhishek Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Finding overlapping times...
Hi Breal I have a list that looks like the following [(10, 100010), (15, 17), (19, 100015)] I would like to be able to determine which of these overlap each other. So, in this case, tuple 1 overlaps with tuples 2 and 3. Tuple 2 overlaps with 1. Tuple 3 overlaps with tuple 1. In my scenario I would have hundreds, if not thousands of these ranges. Any nice pythonic way to do this? This may be of no help, but In 1995 I wrote a C library to do some stuff like this. You're welcome to the code. It might give you some ideas, or you might want to wrap it for Python. If you did that, and had additional energy, you could release the result to the Python community. As someone said, how/what to do depends what you want to do with the resulting data structure once you've processed the inputs. In my case I wanted to be able to read in a large number of ranges and be able to answer questions like is X in the range?. I'm pretty sure I made it possible to add ranges to ignore (i.e., that punch holes in an existing range). I'm also pretty sure I'd have made this run in n log n time, by first sorting all the lower bounds (and maybe sorting the upper ones too) and then walking that list. One use case is e.g., for a printer program command line: print --inc-pages 40-70 --exc-pages 51-52 --inc-pages 100-120 which could use the library to step through the range of pages in a doc and easily check which ones should be printed. There are lots of other uses. Lookup time on those queries was probably log n where n is the resulting number of range fragments once the processing (merging/splitting) of the command line was done. I don't remember, but it took me several days to write the code, so it wasn't completely trivial. I can't offer help beyond the code I'm afraid - I have too much other stuff going on. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Speed of Nested Functions Lambda Expressions
Duncan == Duncan Booth [EMAIL PROTECTED] writes: Duncan Terry Jones [EMAIL PROTECTED] wrote: Duncan Booth wrote: Duncan You'll kick yourself for not seeing it. Duncan If you changed fn_inner to: Duncan def fn_inner(): Duncan a, v = v, a Duncan then you also changed 'a' and 'v' into local variables. Duncan LOAD/STORE_FAST is used to access local variables, LOAD/STORE_DEREF Duncan are used to access variables in an outer scope. Argh, yes :-) [Cue background sound of kicking self] Thanks. Terry -- http://mail.python.org/mailman/listinfo/python-list
Re: Speed of Nested Functions Lambda Expressions
[Referring to the thread at http://mail.python.org/pipermail/python-list/2007-October/463348.html with apologies for top posting (I don't have the original mail)] Duncan Booth wrote: You can use Python's bytecode disassembler to see what actually gets executed here: def fn_outer(v): a=v*2 def fn_inner(): print V:%d,%d % (v,a) fn_inner() import dis dis.dis(fn_outer) 2 0 LOAD_DEREF 1 (v) [snip] When you execute the 'def' statement, the two scoped variables a and v are built into a tuple on the stack, the compiled code object for the inner function is also pushed onto the stack and then the function is created by the 'MAKE_CLOSURE' instruction. This is then stored in a local variable (STORE_FAST) which is then loaded and called. So the function definition is pretty fast, BUT notice how fn_inner is referenced by STORE_FAST/LOAD_FAST whereas a and v are referenced by LOAD_DEREF/STORE_DEREF and LOAD_CLOSURE. The code for fn_inner also uses LOAD_DEREF to get at the scoped variables: I'd like to understand what it is that triggers the use of LOAD/STORE_DEREF versus LOAD/STORE_FAST. This seems dependent on how fn_inner above uses the a and v variables. If I change the print V:%d,%d % (v,a) line to be something like (a, v) = (v, a) or do other simple uses and assignments to a and v, LOAD/STORE_FAST is used. It seems that it's the use of print (in this case) that triggers the use of LOAD/STORE_DEREF in the bytecode. Can anyone shed more light on what in general causes the switch to LOAD/STORE_DEREF? I find it a bit disconcerting that the simple presence of a nested function (even if never used) may trigger significant slowdown in variable access for the rest of the code in the containing function. I'd be happy to know how/when this happens. I know I probably shouldn't care, but I do. Thanks, Terry Jones -- http://mail.python.org/mailman/listinfo/python-list