[Tutor] Variable reference
Peter Otten
__peter__ at web.de
Wed Jul 8 14:54:55 CEST 2015
Steven D'Aprano wrote:
> When writing a script or application, name management is not a big deal.
> But in a library, temporary variables are pollution. They make it harder
> for the users of your library to tell what's part of the API and what
> isn't, and they make "from module import *" less useful. So if I have a
> temporary global variable, I may want to get rid of it once I'm finished
> with it. Here's a snippet from a library module I have, designed to be
> used with * imports:
>
> tmp = set(C0.keys()) & set(C1.keys())
> assert not tmp, 'duplicate control code acronyms: %s' % tmp
> # Special check for SCG abbreviated acronym.
> assert 'SGC' not in C0
> assert 'SGC' not in C1
> # Validate that the ^ and ESC codes are correct.
> for C in (C0, C1):
> for cc in C.values():
> assert cc.code == _code(cc.ordinal), 'failed check: %s' % cc
> del C, cc, tmp
>
> Those three temporary variables, C, cc and tmp, would otherwise hang
> around forever, polluting the namespace and confusing my module's users.
> (My module's users, so far, is mostly me, but I'm easily confused.)
You are not alone with that design. The first thing that came up in the
stdlib was the webbrowser module:
$ cd /usr/lib/python3.4
$ find . -name \*.py -print0 | xargs -0 egrep '\bdel\s+(_|[[:alnum:]])+$' |
head -n1
./webbrowser.py: del cmdline
$
Here's the relevant part of the code where the author manages to del two out
of three helper variables:
"""
# OK, now that we know what the default preference orders for each
# platform are, allow user to override them with the BROWSER variable.
if "BROWSER" in os.environ:
_userchoices = os.environ["BROWSER"].split(os.pathsep)
_userchoices.reverse()
# Treat choices in same way as if passed into get() but do register
# and prepend to _tryorder
for cmdline in _userchoices:
if cmdline != '':
cmd = _synthesize(cmdline, -1)
if cmd[1] is None:
register(cmdline, None, GenericBrowser(cmdline), -1)
cmdline = None # to make del work if _userchoices was empty
del cmdline
del _userchoices
"""
I really don't like that approach; I prefer writing a helper function,
properly marked as an implementation detail, e. g.
def _setup_userchoices(userchoices=None):
"""
>>> _setup_userchoices(["one"])
>>> _tryorder[0]
'one'
>>> _setup_userchoices(["two", "three"])
>>> _tryorder[:3]
['two', 'three', 'one']
"""
if userchoices is None:
userchoices = os.environ.get("BROWSER", "").split(os.pathsep)
for cmdline in reversed(userchoices):
if cmdline != "":
cmd = _synthesize(cmdline, -1)
if cmd[1] is None:
register(cmdline, None, GenericBrowser(cmdline), -1)
_setup_userchoices()
That is easy to test (if this were for real you'd use unit test, not
doctest), and you don't have to decide whether the leftover `cmd` is kept
intentionally or by accident, or be careful to avoid that you tread on names
that *are* part of the API.
That are the advantages for the author/reader of the module. As a user the
extra _setup_userchoices() function doesn't bother me at all.
More information about the Tutor
mailing list