Difficulty with `cmp', still

Tim Peters tim_one at email.msn.com
Wed Sep 22 00:54:24 EDT 1999


[François Pinard]
> Hi, people.  I feel a bit opaque, as I cannot quickly get this
> code to work:
>
>
> # Return the comparison value of two versions, of two file names
> with versions.
> def compare_versions(a, b):
>     a0, a1, a2, a3, a4 = _weights(a)
>     b0, b1, b2, b3, b4 = _weights(b)
>     print dir(__builtins__)
>     cmp = __builtins__.cmp         # because of `import cmp' above
>     return cmp(a0, b0) or a1 - b1 or a2 - b2 or a3 - b3 or a4 - b4

Answers to two questions you didn't ask <wink>:

1. Mucking with double-underscore names is a disease.  There are a few names
like that meant for routine use, primarily special class methods (like
__len__ and __add__).  But most double-underscore names expose obscure
pieces of the internals, and should almost never be used unless you're, say,
writing a Python debugger in Python.  Everyday programs don't need them and
are better off avoiding them.

In this case, I suggest avoiding __builtins__ by changing the way you import
the conflicting name:

    import cmp
    file_cmp = cmp.cmp  # assign a local, non-conflicting name
    del cmp # get the jerk out of our namespace, unshadowing the builtin

or by writing a trivial wrapper with a non-conflicting name:

    def file_cmp(f1, f2, shallow=1):
        import cmp  # the import only affects this function's locals
        return cmp.cmp(f1, f2, shallow)

2. You're familiar with lexicographic comparison of strings (keep going
"left to right" until finding unequal chars).  In Python, strings are
sequences of characters, and lexicographic comparison is used for *all*
builtin sequence types, not just strings.  I don't know what type your
_weights function returns, but it looks like sequences, so barring something
subtle the above is likely equivalent to the simpler:

    return cmp(_weights(a), _weights(b))

> When used in batch, I get "AttributeError: cmp".  When used interactively,
> everything seems to work fine, and `compare_versions' does what it should.
> The `print' statement is a mere try to understanding the problem, and it
> has the effect of adding this line to the output, just before the
> traceback:
> ...
> If I do the same `print' interactively, I get (refilled in this message):
> ...
> So far that I know, I'm not playing tricks nor trying to more clever than
> Python, I would think my code is rather straight.  I'm surely not trying
> to play with the builtin lists, the most I do is trying to get the `cmp'
> from it.  I do not see how or why the builtin list get reduced when my
> code is executed in batch, and why `cmp' is disappearing.  Would someone
> have a suggestion or an idea of what I should study to grasp what is
> happening?

The module __builtin__ contains all the builtin functions compiled into
Python.  But there's a layer of indirection between that and what a module
*sees* as being the builtin namespace, primarily to support a bulletproof
restricted execution mode (e.g., some apps can't afford to let Python code
run the builtin "open" function).  So rather than reference __builtin__
directly, the implementation looks up the related name __builtins__, which
the runtime always arranges to bind to an appropriate mapping object (either
a module or a dict).  It may or may not resolve to the module __builtin__,
or to __builtin__'s global dict.  It will *usually* resolve to one of those
two, but you can't count on that.

The point is that it's a detail of the implementation, and is there for
Python's convenience more than yours <wink>.  Don't touch it, and you won't
get surprised.

The language reference manual's section 4.1 ("Code blocks, execution frames,
and namespaces") is the key to much of Python's semantics (including the
__builtin__ vs __builtins__ distinction), as it defines "what names mean".
So that's the section to study if you want a deep understanding.

if-you-want-a-shallow-one-read-"learning-perl"<wink>-ly y'rs  - tim






More information about the Python-list mailing list