Is the following code bad practice?
for i in some_values:
do_whatever(i)
do_more_things(i)
Somehow, it feels to me like the variable i should remain in the scope to the block inside the for-loop. However python 2.7 lets me reuse it after the loop.
Does python officially supports that feature, or am I abusing the language?
Yes, it's official:
for_stmt ::= "for" target_list "in" expression_list ":" suite
["else" ":" suite]
> The target list is not deleted when the loop is finished
http://docs.python.org/reference/compound_stmts.html#for
Note that a target list after for is far more than just a variable:
for some_list[blah] in...
for some_object.foo in...
for a[:n] in ...:
etc. These things cannot simply disappear after the loop.
Python can feel a bit special when it comes to scoping if you are coming from languages like C or Java. As previous answer says, the code is absolutely valid but I would recommend against it. It creates not particularly readable code and, furthermore, if some_values turns out to be empty, there will be an exception raised on the last line of the code.
So answer to your question is - yes, it is officially supported but in most cases it is not recommended strategy.
Interesting discussion can be found here and also local discussion on SO.
You can use this technique to scan a list for items matching some criteria:
for item in iter:
if interesting(item):
break
else:
raise ValueError('iter is boring.')
handle(item)
Just like #petr said, it sound unnatural. Not because it's allowed that it's natural or you have to use it.
I rather have something like this, although it may not apply to the logic with a break use-case:
for i in some_values:
do_whatever(i)
else:
do_more_things(i)
But this still raise NameError if some_values evaluate to empty, but sounds clearer. It does not give the clear readability of an inner scope but the indentation may suggests it.
But as other said, to answer to the specific OP's question, yes, it's legal.
That's not a feature. As you wrote the variable remains in the scope. It's normal interpreter behavior.
Related
Why is this a valid statement in Python?
"Guido"
This tripped me up with a multi-line string where I did not properly use parens:
# BAD
message = "Guido"
" van Rossum"
# GOOD
message = ("Guido"
" van Rossum")
Is it purely for the repl or is there some other reasoning for it?
Expressions are statements in Python (and most other imperative languages) for several good reasons:
What if a function, like foo() does something useful but also returns a value? foo() would be an invalid statement if the language didn't allow us to implicitly discard the return value.
Docstrings! They are just strings that happen to be the first statement of a module/class/function, so no special syntax was needed to support them.
In general, it's hard for the interpreter to determine whether an expression might have side effects. So it's been designed to not even try; it simply evaluates the expression, even if it's a simple constant, and discards the result.
To check for such mistakes as you mentioned, and many others, pylint can be helpful. It has a specific warning for this very case. However, it seems to not catch the mistake in your exact example code (using PyLint version 2.4.4); might be a bug.
Is there any other code form, that one can both use a function in if statement and get the value of function without executing the function twice?
For example,
There exists a function, fun1(arg), which takes an hour to return its result (The result value can be either None or some int)
and I want to do some further calculation(for example get its squared value) only if the result from fun1 is not None.
This will be done by:
result = fun1(arg)
if result:
result = result * result
Is there any shorter form such as
if (result = fun1(arg)):
result = result * result
in python?
It may be more "clean" in a code manner, it is possible in C/C++ to do the 2nd one. Not in Python to the best of my knowledge. Moreover, the two examples you gave have the exact same needs in term of memory and computation. So it would be totally equivalent to use any of these two.
The two are absolutely identical. So my answer would be, go with your first method that you already know how to code 👍.
Do not over complicate when it is not necessary, it is a good piece of advice in general.
This is coming in a future version of python. See the following PEP
https://www.python.org/dev/peps/pep-0572/
It'll be known as an assignment expression. The proposed syntax is like;
# Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
No you can't do this. Statements in Python not work as expressions in C with ;.
Well the second possible solution you wrote is incorrect since the 'result' variable in the if statement has no preassigned value. I would simply go with the first one...
What you are trying to do in your 2nd code is assignment inside expressions, which can't be done in Python.
From the official docs
Note that in Python, unlike C, assignment cannot occur inside expressions. C programmers may grumble about this, but it avoids a common class of problems encountered in C programs: typing = in an expression when == was intended.
also, see:
http://effbot.org/pyfaq/why-can-t-i-use-an-assignment-in-an-expression.htm
Why use pass instead of something shorter like 0 in Python?
E.g.:
for i in range(10): pass
vs.
for i in range(10): 0
The only two upsides of using pass are:
If using the Python interactive interpreter, it will output 0
pass is more understandable than 0
Is there any other reason behind the use of pass?
Answer: It's probably just there for readability.
I dug through an old version of python's source code (version 0.9.1) and found this in a file called doc/tut.tex:
\subsubsection{Pass Statements}
The {\tt pass} statement does nothing.
It can be used when a statement is required syntactically but the
program requires no action.
For example:
\bcode\begin{verbatim}
>>> while 1:
... pass # Busy-wait for keyboard interrupt
...
\end{verbatim}\ecode
And then this reference to it (without much other documentation) in a file called src/graminit.c:
{269, "pass_stmt", 0, 3, states_13,
"\000\000\000\000\002\000\000\000\000\000\000\000"},
which I guess is just adding pass to a pretty base-level vocabulary of python.
this as well, in src/compile.c:
case pass_stmt:
break;
and an incredibly cool file called src/Grammar which makes some references to it and has a changelog at the top which labels itself "Grammar for Python, version 4" but logs no changes to the pass_stmt.
And I couldn't find anything else. So it seems like pass has been in python since forever, and the only documentation from the beginning of the python project says that it 'does nothing.' So I think we can conclude that it's to do nothing, readably.
Looking at the Zen of Python, you can see some reasons;
Readability counts.
Explicit is better than implicit.
In the face of ambiguity, refuse the temptation to guess.
There is no mistaking what pass is.
While a 0 could mean an integer or a null operation depending on the context. That makes the code harder to read and ambiguous.
Let's say I want to partition a string. It returns a tuple of 3 items. I do not need the second item.
I have read that _ is used when a variable is to be ignored.
bar = 'asd cds'
start,_,end = bar.partition(' ')
If I understand it correctly, the _ is still filled with the value. I haven't heard of a way to ignore some part of the output.
Wouldn't that save cycles?
A bigger example would be
def foo():
return list(range(1000))
start,*_,end = foo()
It wouldn't really save any cycles to ignore the return argument, no, except for those which are trivially saved, by noting that there is no point to binding a name to a returned object that isn't used.
Python doesn't do any function inlining or cross-function optimization. It isn't targeting that niche in the slightest. Nor should it, as that would compromise many of the things that python is good at. A lot of core python functionality depends on the simplicity of its design.
Same for your list unpacking example. Its easy to think of syntactic sugar to have python pick the last and first item of the list, given that syntax. But there is no way, staying within the defining constraints of python, to actually not construct the whole list first. Who is to say that the construction of the list does not have relevant side-effects? Python, as a language, will certainly not guarantee you any such thing. As a dynamic language, python does not even have the slightest clue, or tries to concern itself, with the fact that foo might return a list, until the moment that it actually does so.
And will it return a list? What if you rebound the list identifier?
As per the docs, a valid variable name can be of this form
identifier ::= (letter|"_") (letter | digit | "_")*
It means that, first character of a variable name can be a letter or an underscore and rest of the name can have a letter or a digit or _. So, _ is a valid variable name in Python but that is less commonly used. So people normally use that like a use and throw variable.
And the syntax you have shown is not valid. It should have been
start,*_,end = foo()
Anyway, this kind of unpacking will work only in Python 3.x
In your example, you have used
list(range(1000))
So, the entire list is already constructed. When you return it, you are actually returning a reference to the list, the values are not copied actually. So, there is no specific way to ignore the values as such.
There certainly is a way to extract just a few elements. To wit:
l = foo()
start, end = foo[0], foo[-1]
The question you're asking is, then, "Why doesn't there exist a one-line shorthand for this?" There are two answers to that:
It's not common enough to need shorthand for. The two line solution is adequate for this uncommon scenario.
Features don't need a good reason to not exist. It's not like Guido van Rossum compiled a list of all possible ideas and then struck out yours. If you have an idea for improved syntax you could propose it to the Python community and see if you could get them to implement it.
Can inputting and checking be done in the same line in python?
Eg) in C we have
if (scanf("%d",&a))
The above statement if block works if an integer input is given. But similarly,
if a=input():
Doesn't work in python. Is there a way to do it?
No, Python can't do assignment as part of the condition of an if statement. The only way to do it is on two lines:
a=input()
if a:
// Your code here
pass
This is by design, as it means that assignment is maintained as an atomic action, independent of comparison. This can help with readability of the code, which in turn limits the potential introduction of bugs.
You can't do it. This was a deliberate design choice for Python because this construct is good for causing hard to find bugs.
see #Jonathan's comment on the question for an example