An illustrative example for PEP 3150's statement local namespaces

A task I actually ran into at work this week: get a filtered list of subdirectories, exclude some based on a list of names to be ignored, sort the remainder by their modification times. (This problem was actually also the origin of my recent filter_walk recipe: http://code.activestate.com/recipes/577913-selective-directory-walking/) Translated to 3.x (i.e. the generator's .next() method is replaced by the next() builtin), the code would look roughly like this: # Generate list of candidate directories sorted by modification time candidates = next(filter_walk(base_dir, dir_pattern=dir_filter, depth=0)).subdirs candidates = (subdir for subdir in candidates if not any(d in subdir for d in dirs_to_ignore)) def get_mtime(path): stat_path = os.path.join(base_dir, path) return os.stat(stat_path).st_mtime candidates = sorted(candidates, key=get_mtime) Now, that could theoretically be split out to a separate function (passing base_dir, dir_filter and dirs_to_ignore as arguments), but the details are going to vary too much from use case to use case to make reusing it practical. Even factoring out "get_mtime" would be a waste, since you end up with a combinatorial explosion of functions if you try to do things like that (it's the local code base equivalent of "not every 3 line function needs to be in the standard library"). I can (and do) use vertical white space to give some indication that the calculation is complete, but PEP 3150 would allow me to be even more explicit by indenting every step in the calculation except the last one: candidate_dirs = sorted(candidate_dirs, key=get_mtime) given: candidate_dirs = next(filter_walk(base_dir, dir_pattern=dir_filter, depth=0)).subdirs candidate_dirs = (subdir for subdir in candidates if not any(d in subdir for d in dirs_to_ignore)) def get_mtime(path): stat_path = os.path.join(base_dir, path) return os.stat(stat_path).st_mtime Notice how the comment from the original version becomes redundant in the second version? It's just repeating what the actual header line right below it says, so I got rid of it. In the original version it was necessary because there was no indentation in the code to indicate that this was all just different stages of one internal calculation leading up to that final step to create the sorted list. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Nick Coghlan <ncoghlan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> writes:
Some unrelated feedback to that PEP (very neat idea, IMO): 1. Shouldn't: ,---- | # Current Python (manual namespace cleanup) | def _createenviron(): | ... # 27 line function | | environ = _createenviron() | del _createenviron | | # Becomes: | environ = _createenviron() given: | def _createenviron(): | ... # 27 line function `---- really be ,---- | | # Becomes: | environ = _environ given: | ... # 27 line function that defines _environ `---- What's the point of defining a function in the given clause if you only execute it once? You may just as well run the function code directly in the given clause. 2. In ,---- | # Current Python (early binding via default argument hack) | seq = [] | for i in range(10): | def f(_i=i): | return i | seq.append(f) | assert [f() for f in seq] == list(range(10)) `---- You probably meant "return _i"? 3. In my opinion the explicit early binding syntax is very ugly. Also, doesn't it restrict early binding to just one variable? 4. I think having given blocks default to early binding and other nested scopes not is very counterintuitive (but I don't have any idea of how to best resolve this). Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On Thu, 2011-10-20 at 14:37 +1000, Nick Coghlan wrote:
I think you are loosing me, I just don't see some of the things you've mentioned before in this. And I don't see any advantage in the second version over the first. It's fairly common to build up a result by a chain of results that modifies the result of the previous result, with a final result. And it's not too uncommon to reuse the same name as you go, so... If the given statement set the name of the ongoing calculation, and the suite following it was a series of expressions with no right hand side assignment. Your routine would look as follows. # Generate list of candidate directories # sorted by modification time. def get_mtime(path): stat_path = os.path.join(base_dir, path) return os.stat(stat_path).st_mtime candidate_dirs given: = next(filter_walk(base_dir, dir_pattern=dir_filter, depth=0)).subdirs = (subdir for subdir in candidate_dirs if not any(d in subdir for d in dirs_to_ignore)) = sorted(candidate_dirs, key=get_mtime) Each expression would give candidate_dir a new value and avoid duplicating the result name on each line. It also might be possible to generate optimized byte code as the result could stay on the stack until the block is exited. I could also see this used in a command window...
This does put the item of interest up front like you want, and it also reduces repetition is calculations, but it's quite a different concept overall. Cheers, Ron

Nick Coghlan <ncoghlan-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> writes:
Some unrelated feedback to that PEP (very neat idea, IMO): 1. Shouldn't: ,---- | # Current Python (manual namespace cleanup) | def _createenviron(): | ... # 27 line function | | environ = _createenviron() | del _createenviron | | # Becomes: | environ = _createenviron() given: | def _createenviron(): | ... # 27 line function `---- really be ,---- | | # Becomes: | environ = _environ given: | ... # 27 line function that defines _environ `---- What's the point of defining a function in the given clause if you only execute it once? You may just as well run the function code directly in the given clause. 2. In ,---- | # Current Python (early binding via default argument hack) | seq = [] | for i in range(10): | def f(_i=i): | return i | seq.append(f) | assert [f() for f in seq] == list(range(10)) `---- You probably meant "return _i"? 3. In my opinion the explicit early binding syntax is very ugly. Also, doesn't it restrict early binding to just one variable? 4. I think having given blocks default to early binding and other nested scopes not is very counterintuitive (but I don't have any idea of how to best resolve this). Best, -Nikolaus -- »Time flies like an arrow, fruit flies like a Banana.« PGP fingerprint: 5B93 61F8 4EA2 E279 ABF6 02CF A9AD B7F8 AE4E 425C

On Thu, 2011-10-20 at 14:37 +1000, Nick Coghlan wrote:
I think you are loosing me, I just don't see some of the things you've mentioned before in this. And I don't see any advantage in the second version over the first. It's fairly common to build up a result by a chain of results that modifies the result of the previous result, with a final result. And it's not too uncommon to reuse the same name as you go, so... If the given statement set the name of the ongoing calculation, and the suite following it was a series of expressions with no right hand side assignment. Your routine would look as follows. # Generate list of candidate directories # sorted by modification time. def get_mtime(path): stat_path = os.path.join(base_dir, path) return os.stat(stat_path).st_mtime candidate_dirs given: = next(filter_walk(base_dir, dir_pattern=dir_filter, depth=0)).subdirs = (subdir for subdir in candidate_dirs if not any(d in subdir for d in dirs_to_ignore)) = sorted(candidate_dirs, key=get_mtime) Each expression would give candidate_dir a new value and avoid duplicating the result name on each line. It also might be possible to generate optimized byte code as the result could stay on the stack until the block is exited. I could also see this used in a command window...
This does put the item of interest up front like you want, and it also reduces repetition is calculations, but it's quite a different concept overall. Cheers, Ron
participants (3)
-
Nick Coghlan
-
Nikolaus Rath
-
Ron Adam