Overriding a global

J

Jean-Michel Pichavant

Chris said:
So... it's a bad idea for me to use 'i' many times in my code, with
the same name having different meanings in different places? In
languages with infinitely-nesting scopes (one of Python's great lacks,
imho), I've often had three different variables with the same names,
all perfectly valid, and all doing what they should. It's not just
loop indices - I used to have a piece of code in which 'res' was a
MySQL resource being processed in a loop, and I had three nested
loops. Each time I referenced 'res', it used the innermost available
resource, which was precisely what I wanted. If I'd arbitrarily had to
guarantee that all variable names were unique, I would have had
completely unnecessary fiddling around.

Python wouldn't let you do that with three nested 'res'es in one
function, but you can perfectly reasonably have a global and a local.
It makes perfect sense... which is a good reason for keeping it legal.

ChrisA

Bad ideas :

i = 5

def spam():
for i,v in enumerate([1,2,3,4]):
for i,v in enumerate(['a','b', 'c']):
print i, v
print i,v # bad surprise


good ideas :

# global
nameThatWillNotBeUsedlocally = 'foo'

def spam():
for qtyIndex, quantity in enumerate([5,6,3,1]):
for fruitIndex, fruit in enumerate(['orange', 'banana']):
print fruitIndex, fruit
print qtyIndex, quantity

While a lot of people still use i,j,k,v to handler values and indexes, I
think it's a bad idea. I'm just stating an opinion from my personnal
python experience. I know some people can successfully use the hard way.

JM
 
C

Chris Angelico

Bad ideas :

i = 5

def spam():
 for i,v in enumerate([1,2,3,4]):
  for i,v in enumerate(['a','b', 'c']):
    print i, v
  print i,v # bad surprise

That's my point. It's not safe to do it in Python, because the "inner"
local i is the same as the "outer" local i. Python doesn't have block
scope the way most C-like languages do.

int spam()
{
for (int i=0;i<5;++i)
{
for (int i=2;i<4;++i) write("inner "+i+"\n");
write("outer "+i+"\n");
}
}

Works perfectly, and the only cost is that variables must be formally declared.

In Python, you can kinda fudge that sort of thing with nested functions.

def spam():
q=2 # just to prove that the scopes really are nested
for i in range(5):
def ham():
for i in range(2,4):
print("q = %d, i = %d"%(q,i))
ham()
print("i = %d"%i)

It's somewhat clunky, but it does give the illusion of block scope.
Inners mask outers, outers are visible to inner functions. It's an odd
construct though. Very odd.

So... I think I've figured out how to implement from __future__ import braces.

#define { def innerfunc():
#define } innerfunc()

And there you are, out of your difficulty at once!

ChrisA
 
S

Steven D'Aprano

Bad ideas :

i = 5

def spam():
for i,v in enumerate([1,2,3,4]):
for i,v in enumerate(['a','b', 'c']):
print i, v
print i,v # bad surprise

The bad surprise happens because you are using the same name twice in
*one* namespace, the local scope. This example has nothing to do with
local/global name clashes: the existence of global i is irrelevant.
Python's scoping rules work correctly, and global i is not affected by
the local i.

Programming languages use multiple namespaces so that you don't need to
make your variable names globally unique. There are languages that don't
distinguish between local and global. Python is not one of them. The
programmer should feel free to use local names without worrying too much
if they accidentally use a global name.

Having said that, re-using names isn't *entirely* risk free, because if
you use a global name locally, and then try to *also* access the global
name, you will fail. This is called shadowing, and the problem with
shadowing is when you do it by accident. (Newbies are particularly prone
to this, especially when they call variables "str", "list", etc.) But it
is the "by accident" part that is dangerous: there is nothing wrong with
shadowing globals or builtins when you do it by design.

good ideas :

# global
nameThatWillNotBeUsedlocally = 'foo'

Oh please. Names can be too long as well as too short.

def spam():
for qtyIndex, quantity in enumerate([5,6,3,1]):
for fruitIndex, fruit in enumerate(['orange', 'banana']):
print fruitIndex, fruit
print qtyIndex, quantity

More sensible naming conventions are to be encouraged, but verbose names
just for the sake of verbosity is not. spam() is a five line function; if
the programmer can't keep track of the meaning of loop variables i and j
over five lines, perhaps they should consider a change of career and get
a job more suited to their intellectual prowess. I hear McDonalds is
hiring.

If spam() were larger and more complex, then more expressive names would
be valuable. But in the simple example you give, it just adds noise.
 
J

Jean-Michel Pichavant

Steven said:
Bad ideas :

i = 5

def spam():
for i,v in enumerate([1,2,3,4]):
for i,v in enumerate(['a','b', 'c']):
print i, v
print i,v # bad surprise

The bad surprise happens because you are using the same name twice in
*one* namespace, the local scope. This example has nothing to do with
local/global name clashes: the existence of global i is irrelevant.
Python's scoping rules work correctly, and global i is not affected by
the local i.

Programming languages use multiple namespaces so that you don't need to
make your variable names globally unique. There are languages that don't
distinguish between local and global. Python is not one of them. The
programmer should feel free to use local names without worrying too much
if they accidentally use a global name.

Having said that, re-using names isn't *entirely* risk free, because if
you use a global name locally, and then try to *also* access the global
name, you will fail. This is called shadowing, and the problem with
shadowing is when you do it by accident. (Newbies are particularly prone
to this, especially when they call variables "str", "list", etc.) But it
is the "by accident" part that is dangerous: there is nothing wrong with
shadowing globals or builtins when you do it by design.


good ideas :

# global
nameThatWillNotBeUsedlocally = 'foo'

Oh please. Names can be too long as well as too short.


def spam():
for qtyIndex, quantity in enumerate([5,6,3,1]):
for fruitIndex, fruit in enumerate(['orange', 'banana']):
print fruitIndex, fruit
print qtyIndex, quantity

More sensible naming conventions are to be encouraged, but verbose names
just for the sake of verbosity is not. spam() is a five line function; if
the programmer can't keep track of the meaning of loop variables i and j
over five lines, perhaps they should consider a change of career and get
a job more suited to their intellectual prowess. I hear McDonalds is
hiring.

If spam() were larger and more complex, then more expressive names would
be valuable. But in the simple example you give, it just adds noise.

The next time I'll illustrate meaningful names, I'll write a 3000 lines
function, just to be sure no one states that my point does'nt apply to a
function named spam which only counts from 1 to 3.
And don't answer that the spam function above does not count from 1 to
3, I know it doesn't.

For anyone interested in the actual topic, a good reading is
http://tottinge.blogsome.com/meaningfulnames/#Mult_Meanings


JM
 
J

Jean-Michel Pichavant

Joshua said:
[snip]
Using currentLogger is just padding, in my opinion. *Every *value is
"current<value>".
Not always. I try to keep names on the same object because that object
is supposed to be named that way.
I can change one of the object attribute, but the object named that way
keep being the same.

Class Foo:
self.__init__(self):
self.banana = 5

myFoo = Foo()

Now there's a slight difference between

myFoo = Exception()
and
myFoo.banana = 4

The first statement rebind myFoo to something complitely different.
the second statement change one of the object rightfully named myFoo ..
attribute (not sure about this construct :D )

Int being inmutable, you can rebind a name without changing its meaning.
In regards to a second name - yes this could work and in many cases
would be desirable, but it doesn't really help this circumstance.
[assume, for a moment, a lot of functions used a local logger]
"localLogger" would tell you very little about what the logger
actually /is/. You still have to look that up. [end assumption]
Additionally, this would make changing a logger that uses the default
to a local one /much/ harder. And don't say "but you can just always
make a local copy", as then you lose the advantage of a global.

Typing something like "logger = childLogger(id)" to the start of a
function call *is explicit*, it's clean, and it makes sense to have a
default that's global. You're not appending cruft ("current") and you
have consistency. If you added "logger = globalLogger" to every
function start as well you can argue that it's better. I agree it's
more explicit. But then you lose unneeded if only a small portion of
your code localises logger. But I would recommend it if a large
portion of code used local variants.

AND:

The next time I'll illustrate meaningful names, I'll write a 3000
lines function, just to be sure no one states that my point
does'nt apply to a function named spam which only counts from 1 to 3.
And don't answer that the spam function above does not count from
1 to 3, I know it doesn't.


You're acting in sarcasm to a comment on scale, when you yourself said
that one of my comments was invalid due to names that were scaled down
for exampling. It seems a bit hypocritical to me. That said, not all
functions are long. If the short ones use short names that's fine: I'm
pretty sure you said it's not.

And in regards to the link:
1) __add__ says otherwise (technically, the operator "+"). It's rarely
confused me.
2) That's not what we're discussing. As it said: "As long as the
parameter lists are semantically equal and the desired result is the
same, all is well." They're doing semantically the same thing (to
different log levels) with the same parameter lists and they're not
class methods. You /could/ say that the semantics are different, but
classes act in a context in the same way local variables can be
thought of doing, and semantics are the same for them. Instead of a
different self, it's a different log file/level. Same semantics.
I'd like to argue about that but I won't cause I have the feeling my
lack of ultra precise english would cause me more trouble. Note that I'm
not blaming anyone but me, no sarcasm inside.

JM
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top