If I understand correctly
myvar = a and b or c
gives the same result as
if a:
if b:
myvar = b
else:
myvar = c
else:
myvar = c
so I guess it's more elegant.
I seem to remember seeing this kind of short-circuit assignment statement in JavaScript code. But is it considered good style in Python to use short-circuiting in assignment statements?
Most of the time, you then want to use a conditional expression instead:
myvar = b if a else c
Short-circuiting is very Pythonic however, just be aware of the pitfalls where b is false-y; using short-circuiting will result in different result in that case. Most of the time, you do not want to assign c instead.
Even in that case, you can still get the same result with an adjusted condition:
myvar = b if a and b else c
Short-circuiting is great for defaults:
foo = somevar or 'bar'
or for making sure pre-conditions are met:
foo = somevar and function_raises_exception_if_passed_empty_value(somevar)
This is really an opinion question, but for the most part, the answer is no. It goes against multiple style guides, probably because people tend to think it means "if a is true, use the value of b, otherwise use the value of c" rather than the real meaning, which is what you posted.
You probably want the new-ish conditional expression syntax instead:
myvar = b if a else c
Related
I've code,
var a,b,c,d;
I need to rewrite this in python.
I'm not sure, since I'm new to python, how to define multiple variables in a sinlge line with no assignment.
I thought of doing
> a=None
> b=None
> c=None
> d=None
But it should be in one line
More pythonic way is tuple unpacking:
a, b, c, d = 1, 2, 3, 4
Or if you want to initialize to single value
a = b = c = d = 1
You could also use semi-colon (although not encouraged)
a=1; b=2; c=3; d=4
All of them would work.
You could use tuple unpacking:
a, b, c = 1, 2, 3
But to be honest, it would more Pythonic to do the assignments on separate lines.
The "to a single value" part warrants a little bit of extra explanation.
a = b = c = d = None
a = 1
print(b)
>>> None
The above statement does set the value of each variable to None, but with some other types of values, this may not always be the case. Python is an object-oriented language and if you replace the value None with another type of value that is a Python object you might get results that you don't expect (because Python sets all four variables to literally the same object). Consider:
a = b = c = d = list()
a.append(1)
print(b)
>>>[1]
The reason that the result is different is because a, b, c, and d are all referring to the same list. This is a fundamental concept in Python and a really important one to remember, and shows why making these types of one-line declarations can be potentially problematic.
As others have said, declaring your variables on the go (as you need them) is probably the better way to go overall, as it helps to avoid these types of "less obvious" declaration issues.
This will do it in a single line and several expressions
a=None; b=None; c=None; d=None
This will do it in a single line declaring all vars at once with the same value
a = b = c = d = None
And as pointed out in comments, you could even do it with 0 line since you can just use your vars on the go without prior declaration.
If I write the following in python, I get a syntax error, why so?
a = 1
b = (a+=1)
I am using python version 2.7
what I get when I run it, the following:
>>> a = 1
>>> b = (a +=1)
File "<stdin>", line 1
b = (a +=1)
^
SyntaxError: invalid syntax
>>>
Unlike in some other languages, assignment (including augmented assignment, like +=) in Python is not an expression. This also affects things like this:
(a=1) > 2
which is legal in C, and several other languages.
The reason generally given for this is because it helps to prevent a class of bugs like this:
if a = 1: # instead of ==
pass
else:
pass
since assignment isn't an expression, this is a SyntaxError in Python. In the equivalent C code, it is a subtle bug where the variable will be modified rather than checked, the check will always be true (in C, like in Python, a non-zero integer is always truthy), and the else block can never fire.
You can still do chained assignment in Python, so this works:
>>> a = 1
>>> a = b = a+1
>>> a
2
>>> b
2
a +=1 is a statement in Python and you can't assign a statement to a variable. Though it is a valid syntax in languages like C, PHP, etc but not Python.
b = (a+=1)
An equivalent version will be:
>>> a = 1
>>> a += 1
>>> b = a
As #Ashwini stated, a+=1 is an assigment, not a value. You can't assign it to b, or any variable. What you probably want is:
b = a+1
All the answers provided here are good, I just want to add that you can achieve what you want in a one-line expression, but written in a different manner:
b, a = a+1, a+1
Here you're doing almost the same thing: incrementing a by 1, and assigning the value of a+1 to b - I'm telling 'almost' because here we have two summations instead of one.
In order to make Python look more familiar, I've tried to assign an operator symbol to a variable's name,just for educational purposes: import operator equals = operator.eq
This seems to work fine for equals(a,b) but not for a equals b
Is there a way to express that a equals b instead of a == b
No, Python (and most mainstream languages) does not allow this kind of customization. In Python the restriction is quite intentional — an expression such as a equals b would look ungrammatical to any reader familiar with Python.
Not necessarily, but another SO answer shows how you can use this simple trick to "create" new operators. However, they only work if you surround the operator by | | or by << >>:
equals = Infix(lambda x, y: x == y):
print 2 |equals| 2 # True
The best you can do is
def equals(a,b):
return a == b
equals(1,5)
>> False
or
class my:
value = 0
def equals(self, b):
return self.value == b
a = my()
a.equals(3)
>>False
But you should use the built-in operator for readability. This way, a reader can distinguish, at once, an operator, a function, a symbol (a variable), a member function, etc...
In Python 3, operator.or_ is equivalent to the bitwise |, not the logical or. Why is there no operator for the logical or?
The or and and operators can't be expressed as functions because of their short-circuiting behavior:
False and some_function()
True or some_function()
in these cases, some_function() is never called.
A hypothetical or_(True, some_function()), on the other hand, would have to call some_function(), because function arguments are always evaluated before the function is called.
The logical or is a control structure - it decides whether code is being executed. Consider
1 or 1/0
This does not throw an error.
In contrast, the following does throw an error, no matter how the function is implemented:
def logical_or(a, b):
return a or b
logical_or(1, 1/0)
If you don't mind the lack of short circuiting behaviour mentioned by others; you could try the below code.
all([a, b]) == (a and b)
any([a, b]) == (a or b)
They both accept a single collection (such as a list, tuple and even a generator) with 2 or more elements so the following is also valid:
all([a, b, c]) == (a and b and c)
For more details have a look at the documentation in question:
http://docs.python.org/py3k/library/functions.html#all
I have some variables and I want to select the first one that evaluates to True, or else return a default value.
For instance I have a, b, and c. My existing code:
result = a if a else (b if b else (c if c else default))
Another approach I was considering:
result = ([v for v in (a, b, c) if v] + [default])[0]
But they both feel messy, so is there a more Pythonic way?
Did you mean returning first value for what bool(value)==True? Then you can just rely on the fact that boolean operators return last evaluated argument:
result = a or b or c or default
If one variable is not "defined", you can't access its name. So any reference to 'a' raises a NameError Exception.
In the other hand, if you have something like:
a = None
b = None
c = 3
you can do
default = 1
r = a or b or c or default
# r value is 3
So long as default evaluates to True:
result = next((x for x in (a, b, c, d , e, default) if x))
You could do something like this (in contrast to the other answers this is a solution where you don't have to define the 'missing' values as being either None or False):
b = 6
c = 8
def first_defined(items):
for x in items:
try:
return globals()[x]
break
except KeyError:
continue
print first_defined(["a", "b", "c"])
In order to avoid NameErrors when a, b or c isn't defined: give the function a list of strings instead of variable references (you can't pass non-existing references). If you are using variables outside the 'globals()' scope, you could use getattr with its default argument.
--
If a, b and c are defined, I'd go for something like this (considering the fact that an empty string, None or False evaluate to a boolean False):
a = None
b = 6
c = 8
def firstitem(items):
for x in items:
if x:
return x
break
else:
continue
print firstitem([a, b, c])
Don't know if this works in every case, but this works for this case.
a = False
b = "b"
c = False
default = "default"
print a or b or c or default # b
How about this ?
a=None
b=None
c=None
val= reduce(lambda x,y:x or y,(a,b,c,"default"))
print val
The above prints "default". If any of the inputs is defined, val would contain the first defined input.
If by defined you mean ever assigned any value whatsoever to in any scope accessible from here, then trying to access an "undefined" variable will raise a NameError exception (or some subclass thereof, but catching NameError will catch the subclass too). So, the simplest way to perform, literally, the absolutely weird task you ask about, is:
for varname in ('a', 'b', 'c'):
try: return eval(varname)
except NameError: pass
return default
Any alleged solution lacking a try/except won't work under the above meaning for "defined". Approaches based on exploring specific scopes will either miss other scopes, or be quite complex by trying to replicate the scope-ordering logic that eval does for you so simply.
If by "defined" you actually mean "assigned a value that evaluates to true (as opposed to false)", i.e., all values are actually defined (but might happen to be false, and you want the first true value instead), then the already-proposed a or b or c or default becomes the simplest approach. But that's a totally different (and even weirder!) meaning for the word "defined"!-)