namespaces
Steven D'Aprano
steve at REMOVEMEcyber.com.au
Sun Jul 31 23:05:25 EDT 2005
Rob Williscroft wrote:
> Steven D'Aprano wrote in
> news:pan.2005.07.31.14.30.04.607118 at REMOVETHIScyber.com.au in
> comp.lang.python:
>
>
>>Quoting Rob Williscroft:
>>
>>
>>> > def translate( text )
>>> > import string all=string.maketrans('','')
>>> > badcars=all.translate(all,string.letters+string.digits)
>>> > TABLE = string.maketrans(badcars,'_'*len(badcars))
>>> >
>>> > global translate
>>> > def translate( text ):
>>> > return text.translate(TABLE)
>>> >
>>> > return translate( text )
>>
>>This is a difficult piece of code to understand and maintain.
>
>
> Its 8 lines, of self contained code. It does everyting its supposed to do
> and nothing its not.
I didn't say it didn't work. I didn't say it was buggy.
I didn't say it was too long, or had too many dependencies.
Brevity is not always a blessing -- that's how we get
obfuscated C and Perl programs that fit the most
functionality into the fewest lines.
In this _specific_ case, perhaps I was a little harsh.
I've been badly burnt before with self-modifying code:
once burnt, twice shy. I'm still uncomfortable with
your code, but after spending more time following the
program logic, I'm less uncomfortable with it than I was.
However, see additional comments below.
>>You have
>>a function called translate, which in turn calls the string
>>translate() method. That's okay. But then you have a global variable
>>*also* called translate -- does that refer to the same function?
>
> This is how globals work in python, if you wish to (re)bind to a global
> before reading it at function scope, you need to say so.
Yes, I know that. Sorry, my rhetorical question gave
you the wrong impression. I wasn't questioning how
globals work, I was attempting (poorly) to show the
mental hoops a new programmer unfamiliar with the code
has to jump through to get it straight in his head.
>>Is
>>this self-modifying code? That is a dangerous, hard to debug, hard to
>>understand technique that is too-clever-by-half.
>
> Maybe so, but if true, python as a language is too-clever-by-half.
Most languages can create self-modifying code. That's
not the question. The question is whether developers
should write self-modifying code, not whether language
designers should prohibit it.
>>Then you create a local variable, also called translate, also a
>
> No, that isn't how globals work in python, there is no local called
> translate above.
Terminology conflict: I'm refering to the fact that
your second translate function definition is written
locally to the first top-level translate. My bad: I
should have said nested rather than local.
>>function, which simply calls the translate method of its argument. A
>>waste of a function, when all you need to do is call the pre-existing
>>translate method. If I have understood this code correctly, the
>>*local* translate is bound to the *global* translate, which is the
>>function being defined.
>
> Close but there isn't, and never was, a *local* translate function.
Agreed: I meant nested.
>>And lastly, just to complete the confusion, the original function
>>itself appears to call itself -- presumably the rebound copy that
>>points to what was the local copy -- recursively, with the same
>>argument.
>
>
> def f()
> global g
> def g():
> return "something"
> return g()
>
> f() is a function that (re)creates a global function g() and calls it.
> Is it just that translate() rebinds itself that is too much, or do you
> object to f() too ? I do object to f() but only because its useless.
Yes, unless there is a very good reason for rebinding g
dynamically, I would be very cautious of using that
technique.
It means that g changes its behaviour, not because of
the input it gets, but because of when it is called. g
does a certain thing, then in some obscure corner of
your program, hardly noticed, some function or method
calls f and suddenly g acts completely differently.
In principle, you the developer may not even be able to
predict when this change of behaviour will happen. It
comes with the considerable risk that your developers
are desperately trying to read and understand g as it
first was, and not understanding why it is acting
differently. It is a technique that begs to be misused:
if you think you can write confusing, baroque code with
GOTO, you can do worse with dynamically rebinding
functions and self-modifying code.
Just as there are good uses for GOTO, I am happy to
admit that there are good cases for self-modifying
code. But not for something as trivial as creating an
alias for a string method.
All meta-programming should be used with care, and only
when the more, shall we say traditional, alternatives
have serious deficiencies. At the very least it should
be documented in detail.
>>That's a nifty piece of code for showing how clever you are at writing
>>mildly obfuscated code. But for commercial use, where the code has to
>>be maintained and debugged, it is a terrible idea.
>
> 8 lines of self contained code are a terrible idea !, you have IMO a
> very strange idea of what is or isn't maintainable.
No. I work for an IT company, and I know the sort of
code we are called on to maintain, and the sort of
developers that are available.
It isn't that eight lines are too many, but that the
behaviour of the function changes. The fact that it is
self-modifying just makes it even less intuitive. Our
human intuition of behaviour is that things act a
certain way, and while the behaviour should change
depending on the input, it shouldn't change radically
from one time to the next.
For example, we understand that a ball thrown at the
ground will bounce at a different angle depending on
the angle it was tossed at, but we would be shocked to
see it bounce the first time, then stick to the ground
the second, then start bouncing again the tenth time we
toss it.
We intuitively understand changes in state, eg we have
little problem understanding rebinding x from a float
to a string, because that's like changing a glass that
holds water to broken shards that don't (except better:
we can reverse the transformation of x). But we have
little intuitive about glasses that hold water, then
don't hold water -- that's why we get worried and
confused when people suddenly start acting out of
character: behaviour changes, without any visible
change of state.
Any programming technique that acts against human
intuition about how objects "should" behave will cause
many developers confusion and difficulty, and anything
which causes developers confusion and difficulty will
generally lead to buggy code and always to higher
costs. (We have to pay our developers for their time
when they are confused, not just when they are coding
in the zone.)
That's why meta-programming and self-modifying code
should be used with care, and documented well. You
might understand it, but the next guy who comes along
to maintain your code is going to be faced with the
code equivalent of somebody acting out of character.
> Is using generators and decorators a terrible idea too ?
Generators don't mysteriously change behaviour, except
in the limited sense that they halt when they run out
of data, and that is perfectly intuitive. Halting when
you run out of data is so intuitive that one common bug
in code is to forget to program in a halting condition,
especially in recursive functions but even in while
loops -- we just expect that it will stop when it is done.
The principle of how generators work takes a little
understanding, but once you understand the principle,
understanding any specific generator is no harder than
understanding the non-generator equivalent code, and
probably much easier.
Decorators, well, I've never used them, and I still
don't understand why people seem so excited about them,
so I don't have an opinion.
--
Steven.
More information about the Python-list
mailing list