Symbols as parameters?

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Fri Jan 22 13:35:40 CET 2010


On Fri, 22 Jan 2010 09:12:46 +0100, Martin Drautzburg wrote:

> Defining those symbols at the module level is absolutely fine with me.
> The namespace pollution is indeed my biggest worry. You see, I want to
> be able to type in lots of lines with little effort. Preferably I would
> want to type
> 
>         move up
> 
> I could still live with
> 
>         move(up)
> 
> But then I need to import "from Movements import directions" or
> something like that. 

That's one line. It's hardly any cost, and it makes the code 
understandable: the reader can see exactly where directions comes from. 
This is a Good Thing.


> If another module defines "up" in some different
> way, I am in trouble. 

Only if you do this:

from movements import up, down, left, right
from wossnames import up, down, left, right

The solution to this problem is simple: don't do it.

Honestly, you're over-thinking this problem. Such names clashes can't 
happen by accident. They can happen through carelessness, but that's no 
different from this:

x = 42
# ... much later on ...
x = 23
# ... and later ...
assert x == 42

The only time such name clashes can happen by accident is if you do 

from movements import *

which is precisely why that form of import is not recommended.


> To circumvent this I would have to "import
> Movements", but then I's have to write
> 
>         move(directions.up)

You'd have to say "import directions" to write "move(directions.up)".

Another solution:

import directions
up = directions.up
move(up)


> This is so noisy, I'd rather write
> 
>         move ("up")
> 
> I don't like the quotes. I don't mind that "up" is a string (as someone
> suspected), what I dislike is that "up" was created ad-hoc by the
> caller. Could it be move("UP") as well? You could not tell without
> looking at the code of move(). 

Or the documentation.

This is an API design decision the designer has to make. Should the move 
function use strings or integers or something else to specify the 
direction? If strings, should they be case-sensitive or insensitive? 
There's no right or wrong answer, all of these things have arguments in 
favour and against.



[...]
>> Either way, when you go to *use* the direction, you're still passing a
>> string. There's no difference between:
>> 
>> move(m.UP)
>> 
>> and just
>> 
>> move("up")
> 
> The difference is that move(m.UPx) would automatically raise an
> attribute error 

and move(UPx) would raise NameError, and move(U P) would raise 
SyntaxError. What's your point?


> wheras move("upx") requires extra code in move() to
> raise an exception. 

What extra code? Surely move already validates its input? Surely it looks 
something like this?

def move(direction):
    if direction == 'up':
        foo
    elif direction == 'down':
        bar
    elif direction == 'left':
        baz
    elif direction == 'right':
        foobar
    else:
        raise ValueError("invalid direction")

(or any variation that does the same sort of thing). The point is, you 
have to validate that direction is a valid direction anyway -- you can't 
trust that the user will only pass valid directions. There's nothing 
stopping the caller from saying move([42, 23]) or move(None), so you have 
to validate the argument inside the function anyway.


> move("up") just looks sematically wrong to me, in
> contrast len("up") is correct, because it really is an operation on
> Strings. When the caller writes move(up) should should not (need to)
> know what "up" really is behind the scenes.

Again, I point you to Ben Finney's enum module, which I think is exactly 
what you want.





-- 
Steven



More information about the Python-list mailing list