I have studied many programming languages and am amused when people attack 
python as if every other language is somehow more optimal.

Cameron and others have provided examples but look at positives AND negatives.

Yes code like: num = int(num)

does look a tad off as it reuses the same name for something that is actually 
in many ways different. You could easily use a new name. But then you would 
have TWO variables in your name space and the old one would not be garbage 
collected unless you explicitly removed it. If you follow this path, would you 
suggest not writing: X = X + 1 either? 

It is actually a fairly common practice in many languages to have code like 
this:

Var = read in something from a file and make some structure like a data.frame
Var = remove some columns from the above thing pointed to by Var
Var = make some new calculated columns ditto
Var = remove some rows ...
Var = set some kind of grouping on the above or sort it and so on.

As you go along you may keep transforming but have no need for earlier results, 
just the new and improved one. So why keep changing names? Some languages 
support not having any names for intermediate results by using nested function 
calls or pipelines.

The reality is that most languages have a series of what I loosely would call 
environments or name tables and as they run, new ones regularly get created and 
removed and even the order of them may change. The algorithm for searching for 
a name varies and can be in some sense linear or more complex. When looking in 
context for a function name, you may follow a different trail or the same one 
as for a variable holding a string and in some implementations the same name 
shadows and in others does not. Wait till you try to figure out the diamond 
pattern of inheritance when you create classes that depend on multiple other 
classes ad nauseum and you try to call a method and it tries to find the one 
you wanted by searching backwards in an interesting way. Many other languages 
decided to just not allow multiple inheritance!

How can you write a recursive function without this kind of variable shadowing? 
Each invocation of a function places the internal namespace in front of the 
parent so the meaning of a variable name used within is always backed by  all 
the iterations before it. But with some kinds of closures, a function may be 
gone and yet variables it had within it persists. Lots of odd examples more 
make this a complex set of CHOICES. 

So what if you suggest we allow re-use of names but WARN you. OK, Python is a 
largely interpreted language. Normally if you ask to use a variable called X, 
it starts a search in the nearest place then the next one and so on till it 
finds it or fails. In many programs, variables are fairly local and found 
easily. But if you want, you can search dozens or hundreds of places and find 
each and every use of X visible at THIS moment and tell the user there are 82 
places it can be seen and here they are. Now what? The first 50 places may be 
in other instances of the recursive function and you have already been warned 
this way 49 times and soon will be warned a few more as it continues to recurse 
as it travels down a tree or graph structure quite legitimately. Some of the 
other  places X may be in use are in some package in a part you are not even 
using indirectly or in middle of a for-loop as a token variable and so on. I 
suspect that 99.99% of the time re-using a name has no negative consequence. 
Making someone keep choosing names like X12346 because there is somewhere an 
X12345 seems a tad silly. But why go to all that expense at run-time versus in 
some lint program?

I recently studied a language called GO that goes to some length to protect  
the user from themselves and often at great expense to getting programming 
done. It won't compile a program if it declares a variable and does not use it. 
Fair enough but I often want to write a sort of shell of a program and check as 
I go to make sure it works before adding more. If I will need 5 variables, I 
might declare them up-front then work on interactions and calculations that 
only include some of them and later I plan on connecting the rest. Nope, it 
won't let me unless I use it as in a print statement or comment it out or just 
remove it. Ah, but then it is not happy I did not initialize it so I set it to 
zero or something. Later, when I finally use it as intended, I need to remove 
the junk. 

Some of this annoyance is so common that they had to come up with a way to shut 
it up. Consider this line:

Result, err = functionA(args)

It returns two arguments. Sometimes it seems silly to worry about the error but 
you MUST write a line of code like:

If (err != nil) {
        Do_something
}

Other times you just care if it worked and don't want the result. So placing an 
_ for one or the other is a signal to shut the darn compiler up that you are 
DELIBERATELY ignoring the return value you did not want!

There are many other places the darn thing tries to protect you and insists you 
write code the hard way. Some are not unreasonable and some I argue are. Their 
method of trapping errors deliberately chose to not do the fairly common 
try/catch method lest it make programmers stop worrying about causing a panic, 
for example, or even depending on it. Their switch statement does not flow 
through to the next item because they worry people might forget the break 
statement so instead, they have a way to specify when you want execution to 
fall through. This last one is an example maybe of something more reasonable.

So, yes, there are languages with philosophies that may help users avoid some 
common mistakes. Use them if you wish. But some of these have a longer learning 
curve and generate lots of frustration and may make some people stop learning 
to program because it takes forever to get something to compile, let alone run.

In my experience, often the work-around they supply to do things is far more 
complex than just providing some freedom and hoping you do not abuse it. In 
interpreted languages, you can really do some weird things if you know more, 
including access to the actual names of variables and the ability to change 
them on the fly and so on and yes, the ability to make weird mistakes.

There can be a happy medium. Supply a function call a user can place anywhere 
in the code that asks if there is an "X" defined at this point in time before 
they use it. And, if there is, tell me about all of them as in if I remove one 
X, is there now another. But will that work the way you want so you can choose 
another name if that bothers you?

My guess is no. The same function containing your test might be called from 
somewhere else in your code and there is does mask something or is masked 
deeper. You may be using modules that change with some new release and have 
variables that now conflict with yours. Some identifiers are so common that the 
order you load packages/modules may determine which one is uppermost.

I often program in R and I do get messages when say a function called select() 
in package B is loaded and shadow the same name in package A or the base.  If 
it matters, I am free to call A::select() or B::select so the names no longer 
clash at that point or I use other techniques. So dynamic changes as packages 
are updated are possible even if my lint method once declared things to be 
fine. So should we force people to use fully qualified names like the above or 
worse?

Python is not perfect, Frankly, a good chunk of the features in python added to 
it as an interpreted language seem to work pretty well in various compiled 
languages I have studied. Many features do not require the interpreter and can 
now be done in other ways. Designers make choices and some are a matter of 
taste. Arguably Python is best for things like prototyping or even learning but 
arguably it is not as efficient for programs that run fairly simple algorithms 
constantly where running faster or using less memory can mean major savings. 
So, yes, often a program written in Python and shown to work, may be rewritten. 
But the opposite can be true as people give up trying to use a restrictive 
environment when the needs keep changing and a more flexible scripting language 
allows rapid changes. 

Python is not designed or planned the way some want and if that bothers them, 
use something else when told by others that the language was designed to do 
what it is supposed to and needs no change.

On another forum someone is asking for R to not have just an NA variable when 
something is missing, but as many variants as needed to record why it is 
missing such as ILLEGIBLE or DELAYED or RAN_OUT_OF_TIME and so on. The claim is 
that many social science applications need this and some other 
programs/languages  sort of support it. Fair enough but again, the design 
way-back-when saw no need for this and lots of drawbacks and although we 
brainstormed lots of ideas on how to attach metadata to a vector to support 
this, it often breaks down as most existing functions toss it away so you may 
have to redo tons of code very carefully to preserve the data or even reapply 
it after a change. Something as simple as converting a vector from character 
strings to their integer values will drop the changes. So, again, if this is 
that important, use something else and do NOT suggest diddling with unused bits 
in one implementation of NA to store your additional metadata as there is no 
guarantee it will get the result you want and it is not a documented guaranteed 
implementation and may break lots of other existing code.

I am not discouraging anyone from asking questions and even suggesting changes. 
I simply suggest when others push back and tell you it was designed this way 
for darn good reasons and many LIKE IT the way it is, ...


-----Original Message-----
From: Python-list <python-list-bounces+avigross=verizon....@python.org> On 
Behalf Of Cameron Simpson
Sent: Monday, May 24, 2021 5:34 AM
To: python-list@python.org
Subject: Re: learning python ...

On 24May2021 08:21, hw <h...@adminart.net> wrote:
>On 5/24/21 12:03 AM, Cameron Simpson wrote:
>>On 23May2021 21:02, Stestagg <stest...@gmail.com> wrote:
>>>On Sun, 23 May 2021 at 20:37, hw <h...@adminart.net> wrote:
>>>>I don't know about shadowing.
>>>
>>>Shadowing is effectively saying “within this bit of code, (scope) I’m 
>>>going to use an already-used name for my own value”
>>
>>An example might make this clearer:
>>
>>     x = 1 # global variable
>>
>>     def f(a):
>>         x = a * 2
>>         return x
>>
>>Inside the function f() the name 'x" shadows the global "x"; 
>>references to "x" are to the function's local vairable. Which is very 
>>desireable.
>
>If it works that way, I would consider it an entirely different 
>variable.  Is there a way to access the global x from within a function 
>without transferring it through parameters of the function?
>Than can also sometimes be useful.

Sure. You can declare a name like this:

    def f(a):
        global x  # find x in the global namespace (the module)
        x = a * 2
        return x

This is pretty rare and usually discouraged. Of there are times when it is 
useful.

Note that in this function:

    x = 1
    y = 2

    def f(a):
        x = 3
        print(x, y)

"x" is local, because the function contains an assignment to it. "y" 
comes from an outer scope (in this case, the global scope) because there's no 
assignment to it.

>>As Stestagg has mentioned, there are also tools called linters which 
>>warn you about issues like this. Tools like pyflakes, pylint, 
>>pycodestyle all inspect your code for a wide variety of potential 
>>errors and discouraged habits.  Not to mention tools like mypy which 
>>do type validation.
>
>So you're saying one can't really go without those unless you want to 
>take the risk?

Self restraint and developing good habits does 99% of the work. Linters are 
great for catching various accidents.

[...]
>I'm not saying it shouldn't be allowed to defeat or to re-define stuff, 
>only that it shouldn't go through quietly.

Well, most of us use linters to exhibit that noise, rather than requiring the 
code to be littered with special directives.

I usually code without much linter fuss until I've got the latest batch of work 
(eg feature or fix) ready, commit the changes, then lint vigorously and commit 
that polish before merging with the main line of code.

Finally, consider this code:

    num = input("Enter a number: ")
    num = int(num)

input() returns a string, which would need converting to a number before some 
numeric stuff. Plenty of people write the above. To my mind, this is also a 
kind of shadowing, and I like this instead:

    num_s = input("Enter a number: ")
    num = int(num_s)

where the "_s" indicates quietly that this variable holds a string.

Is that shadowing to your mind? Or not? If yes, should the language emit noise 
there, too? Remembering that Python _values_ are strongly typed, though the 
variables are not (a variable might reference any kind of object, as above).

I wouldn't say that your opinion would be wrong regardless of what side of this 
question you come down on, but Python's chosen a side: noise for nonsensical 
things, but not noise for dubious things. But plenty of linters to complain 
about dubious things.

Cheers,
Cameron Simpson <c...@cskk.id.au>
--
https://mail.python.org/mailman/listinfo/python-list

-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to