Suggestions for python 2

Edward Welbourne eddy at vortigen.demon.co.uk
Mon Jan 17 19:52:45 EST 2000


OK, some examples would be a good idea, and Evan has shown me some ways
of saying tunneling clearer ... Evan has at least one further change he
wants here but I'd rather leave it to him to describe ... if only for
the hope that he'll express why this is a useful idea - albeit he spells
it different - and he expresses it better than I do.
I only know how to design and implement stuff.
Computers understand my better than folk do.

Three new kinds of argument come into being (with my given spelling):
  * safe tunnel: after both * and **
  * keyword-only: after * but not **
  * positional-only: after ** but not *

I shall not discuss the last: they arise only because `the obvious'
structure (Evan may disagree on that) leads to them, but I can't think
what they'd be for.  I'd be delighted to hear if anyone else can ...

First, tunnelling, then some keyword-only stuff.

Safe tunnel:

You want to let your function see some value you can compute, but you
want it to be able to take arbitrarily many positional args; or you need
to be sure your caller can't mess with one of your args.

  def filename(*fragments, **, root=os.getcwd()):
      """Resolves relative paths."""
      # Assume os.path.join joins arbitrarily many fragments.
      return apply(os.path.join, (root,) + fragments)

  def register(name, callback, *, **, book=registry, checker=check_callback):
      grief = checker(name, callback)
      if grief: raise TypeError, 'Unsuitable call-back'
      book[name] = callback

Having someone over-ride the book argument wouldn't be our problem -
it'd mess up their registry, we don't care - so book doesn't need to be
a safe tunnel, an ordinary keyword parameter would do: but we're not so
calm about them messing with the checker we use before storing stuff in
*our* registry.

Keyword-only:

You want to be able to configure some aspect of your function's
behaviour, but you want it to accept arbitrarily many of the arguments
it primarily accepts (and you don't want to have to supply them as a
list, because your users *are* going to forget and/or find it
anti-intuitive).

Have you ever wanted to be able to call string.joinfields without
needing to pack the args up as a list ?

  def join(*fragments, glue=' '): 
      return string.joinfields(fragments, glue)

or wanted to pass popen its command in argv form, and it wouldn't hurt
to be obliged to give mode and bufsize in keyword form, i.e.
  def popen(*argv, mode='r', bufsize=2048): ...
so that some entries in argv can have spaces in them without obliging
the caller to construct a suitably-quoted string,
  popen('sed', '-e', r's:\\:/:', filename)
as opposed to
  os.popen(r'sed -e "s:\\:/:" "%s"' % filename)
and how many folk might have forgotten that filename might be
r'\\samba\shared programs\path builder.com', so missed off the "" round
the %s ?

Or you just want to insist on keyword-form for some of your arguments,
e.g. (future-proofing) because any code which relies on its positional
order would be an albatross round your neck when, at a later date, you
want to add an optional argument before it that really does have a right
to be positional.  It's bad enough knowing you'll always have to support
the keywords that seemed like a good idea in the first version without
having them prevent you using the much better approach that came along
later.  There's loads of places where obliging your callers to use
keyword-arguments would make life much easier for folk reading their
code; and I care deeply about maintainability, which this aids.

Coming up with examples of future-proofing is hard: I'd have to find
Real Code in which I'd needed the functionality, but I could only find
that if I had used it which, of course, I didn't.  It wasn't available.
Especially as most of what I've ever written is 1.3-compatible, and
where I need functionality I don't have I tend to route round the need
for it, so that the resulting code contains no hint of what I (somehow)
avoided needing.  Here's two examples out of a trawl:

class File:
    # ...
    def lock(self, *, limit=None,):
        # ...

some day, we may be adding configuration options to File.lock() (well,
OK, File.__meth__.lock) but all we know as we implement our first cut at
it is that we're going to allow the caller to limit how long it waits
for the file to come free before giving up.  We know we're probably
going to have some fancy options - at least on OSes which don't support
locking functionality of their own - for identifying how we'll do the
locking; but we're only interested, in the first cut, in something
that'll work.  So we insist that the limit argument be given as a
keyword, so that code using the first version can be supportable by
later versions, without messing up the call-interface of File's .lock in
later versions.

def rmdir(name, *, recurse=None): ...

OK, so we're happy about calls to rmdir('~/junk') but who on earth is
going to understand rmdir('~/junk', 1) - what on earth does that 1 mean ?
so we *force* the caller to make itself intelligible by saying
rmdir('~/junk', recurse=1), which at least more of the code's readers will
understand.  [Aid to maintainability.]

There are more examples in python.py, which may be more intelligibly
read (given the high verbosity levels of my comments and docs) as
justthecode.py, linked to from
http://www.chaos.org.uk/~eddy/dev/toy/python2.html and in the same
directory as it.  Sorry, still no py2html prettified forms.

Examples of using generators will have to follow when I have more time.
However: they have to exist because what else is `class', and the
initspace generator illustrated (aka submodule under Novelties) is the
`most obvious' example of a useful generator - you get to assign to and
def whatever names you like within its suite, thereby creating an
immutable sub-domain of whatever context did it, with a minimum of fuss.

	Eddy.
--
Recipient-list truncated to python-list,




More information about the Python-list mailing list