Re: [Python-checkins] CVS: python/dist/src/Lib sre_constants.py (etc)
can anyone explain why it's a good idea to have totally incomprehensible stuff like __all__ = locals().keys() for _i in range(len(__all__)-1,-1,-1): if __all__[_i][0] == "_": del __all__[_i] del _i in my code? Annoyed /F
Fredrik> can anyone explain why it's a good idea to have totally Fredrik> incomprehensible stuff like Fredrik> __all__ = locals().keys() Fredrik> for _i in range(len(__all__)-1,-1,-1): Fredrik> if __all__[_i][0] == "_": Fredrik> del __all__[_i] Fredrik> del _i Fredrik> in my code? Please don't shoot the messenger... ;-) In modules that looked to me to contain nothing by constants, I used the above technique to simply load all the modules symbols into __all__, then delete any that began with an underscore. If there is no reason to have an __all__ list for such modules, feel free to remove the code, just remember to also delete the check_all() call in Lib/test/test___all__.py. Skip
"SM" == Skip Montanaro <skip@mojam.com> writes:
Fredrik> can anyone explain why it's a good idea to have totally Fredrik> incomprehensible stuff like Fredrik> __all__ = locals().keys() for _i in Fredrik> range(len(__all__)-1,-1,-1): if __all__[_i][0] == "_": del Fredrik> __all__[_i] del _i Fredrik> in my code? SM> Please don't shoot the messenger... ;-) SM> In modules that looked to me to contain nothing by constants, I SM> used the above technique to simply load all the modules symbols SM> into __all__, then delete any that began with an underscore. If SM> there is no reason to have an __all__ list for such modules, SM> feel free to remove the code, just remember to also delete the SM> check_all() call in Lib/test/test___all__.py. If __all__ is needed (still not sure what it's for :-), wouldn't the following one-liner be clearer: __all__ = [name for name in locals.keys() if not name.startswith('_')] Jeremy
If __all__ is needed (still not sure what it's for :-), wouldn't the following one-liner be clearer:
__all__ = [name for name in locals.keys() if not name.startswith('_')]
But that shouldn't be used in /F's modules, because he wants them to be 1.5 compatible. Anyway, filter(lambda s: s[0]!='_', dir()) is shorter, and you prove that it isn't faster. :-) --Guido van Rossum (home page: http://www.python.org/~guido/)
"GvR" == Guido van Rossum <guido@digicool.com> writes:
If __all__ is needed (still not sure what it's for :-), wouldn't the following one-liner be clearer:
__all__ = [name for name in locals.keys() if not name.startswith('_')]
GvR> But that shouldn't be used in /F's modules, because he wants GvR> them to be 1.5 compatible. Anyway, filter(lambda s: s[0]!='_', GvR> dir()) is shorter, and you prove that it isn't faster. :-) Well, if he wants it to work with 1.5.2, that's one thing. But the list comprehensions is clear are short done your way: __all__ = [s for s in dir() if s[0] != '_'] Jeremy
Fredrik> can anyone explain why it's a good idea to have totally Fredrik> incomprehensible stuff like
Fredrik> __all__ = locals().keys() Fredrik> for _i in range(len(__all__)-1,-1,-1): Fredrik> if __all__[_i][0] == "_": Fredrik> del __all__[_i] Fredrik> del _i
Fredrik> in my code?
Please don't shoot the messenger... ;-)
I'm not sure you qualify as the messenger, Skip. You seem to be taking this __all__ thing way beyond where I thought it needed to go.
In modules that looked to me to contain nothing by constants, I used the above technique to simply load all the modules symbols into __all__, then delete any that began with an underscore. If there is no reason to have an __all__ list for such modules, feel free to remove the code, just remember to also delete the check_all() call in Lib/test/test___all__.py.
Rhetorical question: why do we have __all__? In my mind we have it so that "from M import *" doesn't import spurious stuff that happens to be a global in M but isn't really intended for export from M. Typical example: Tkinter is commonly used in "from Tkinter import *" mode, but accidentally exports a few standard modules like sys. Adding __all__ just for the sake of having __all__ defined doesn't seem to me a good use of anybody's time; since "from M import *" already skips names starting with '_', there's no reason to have __all__ defined in modules where it is computed to be exactly the globals that don't start with '_'... Also, it's not immediately clear what test___all__.py tests. It seems that it just checks that the __all__ attribute exists and then that "from M import *" imports exactly the names in __all__. Since that's how it's implemented, what does this really test? I guess it tests that the import mechanism doesn't screw up. It could screw up if it was replaced by a custom import hack that hasn't been taught to look for __all__ yet, for example, and it's useful if this is caught. But why do we need to import every module under the sun that happens to define __all__ to check that? --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido> Adding __all__ just for the sake of having __all__ defined Guido> doesn't seem to me a good use of anybody's time; since "from M Guido> import *" already skips names starting with '_', there's no Guido> reason to have __all__ defined in modules where it is computed to Guido> be exactly the globals that don't start with '_'... Sounds fine by me. I'll remove it from any modules like sre_constants that don't import anything else. Guido> Also, it's not immediately clear what test___all__.py tests. hmmm... There was a reason. If I think about it long enough I may actually remember what it was. I definitely needed it for the first few modules to make sure I was doing things right. I eventually got into this mechanical mode of adding __all__ lists, then adding a check_all call to the test___all__ module. In cases where I didn't construct __all__ correctly (say, somehow wound up with two copies of "xyz" in the list) it caught that. Okay, so I'm back to the drawing board on this. The rationale for defining __all__ is to prevent namespace pollution when someone executes an import *. I guess definition of __all__ should be restricted to modules that import other modules and don't explictly take other pains to clean up their namespace. I suspect test___all__.py could/should be removed as well. Skip
can anyone explain why it's a good idea to have totally incomprehensible stuff like
__all__ = locals().keys() for _i in range(len(__all__)-1,-1,-1): if __all__[_i][0] == "_": del __all__[_i] del _i
in my code?
Ask Skip. :-) This doesn't exclude anything that would be included in import* by default, so I'm not sure I see the point either. As for clarity, it would've been nice if there was a comment. If it is decided that it's a good idea to have __all__ even when it doesn't add any new information (I'm not so sure), here's a cleaner way to spell it, which also gets the names in alphabetical order: # Set __all__ to the list of global names not starting with underscore: __all__ = filter(lambda s: s[0]!='_', dir()) --Guido van Rossum (home page: http://www.python.org/~guido/)
Fredrik Lundh <fredrik@effbot.org>:
for _i in range(len(__all__)-1,-1,-1):
On a slightly wider topic, it might be nice to have a clearer way of iterating backwards over a range. How about a function such as revrange(n1, n2) which would produce the same sequence of numbers as range(n1, n2) but in the opposite order. (Plus corresponding xrevrange() of course.) Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+
[/F]
can anyone explain why it's a good idea to have totally incomprehensible stuff like
__all__ = locals().keys() for _i in range(len(__all__)-1,-1,-1): if __all__[_i][0] == "_": del __all__[_i] del _i
in my code?
I'm unclear on why __all__ was introduced, but if it's gonna be there I'd suggest: __all__ = [k for k in dir() if k[0] not in "_["] del k If anyone was exporting the name "k", they should be shot anyway <wink>. Oh, ya, "[" has to be excluded because the listcomp itself temporarily creates an artificial name beginning with "[".
[k for k in dir()] ['[1]', '__builtins__', '__doc__', '__name__'] ^^^^^ dir() # but now it's gone ['__builtins__', '__doc__', '__name__', 'k']
Tim Peters writes:
Oh, ya, "[" has to be excluded because the listcomp itself temporarily creates an artificial name beginning with "[".
Wow! Perhaps listcomps should use names like _[1] instead, just to reduce the number of special cases. -Fred -- Fred L. Drake, Jr. <fdrake at acm.org> PythonLabs at Digital Creations
[Tim]
Oh, ya, "[" has to be excluded because the listcomp itself temporarily creates an artificial name beginning with "[".
[Fred L. Drake, Jr.]
Wow! Perhaps listcomps should use names like _[1] instead, just to reduce the number of special cases.
Well, it seems a terribly minor point ... so I dropped everything else and checked in a change to do just that <wink>. every-now-&-again-you-gotta-do-something-just-cuz-it's-right-ly y'rs - tim
participants (8)
-
Fred L. Drake, Jr.
-
Fredrik Lundh
-
Greg Ewing
-
Guido van Rossum
-
Jeremy Hylton
-
Skip Montanaro
-
Tim Peters
-
Tim Peters