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