Python Ternary Operator Without else
Asked Answered
R

9

117

Is it possible to do this on one line in Python?

if <condition>:
    myList.append('myString')

I have tried the ternary operator:

myList.append('myString' if <condition>)

but my IDE (MyEclipse) didn't like it, without an else.

Roundtree answered 30/8, 2012 at 14:56 Comment(1)
You mean like a ternary operator that doesn't have three parts? Isn't this a contradiction in terms?Mountainous
I
166

Yes, you can do this:

<condition> and myList.append('myString')

If <condition> is false, then short-circuiting will kick in and the right-hand side won't be evaluated. If <condition> is true, then the right-hand side will be evaluated and the element will be appended.

I'll just point out that doing the above is quite non-pythonic, and it would probably be best to write this, regardless:

if <condition>: myList.append('myString')

Demonstration:

>>> myList = []
>>> False and myList.append('myString')
False
>>> myList
[]
>>> True and myList.append('myString')
>>> myList
['myString']
Intercom answered 30/8, 2012 at 15:5 Comment(8)
While this answer is technically correct, it's not a good programming practice. Since Python aims to be a language that's easily readable, this code would be considered non-Pythonic.Asseverate
@LS: I agree, that's why I said it would probably be best to just use an if statement. But I modified the answer a bit to make that clearer.Intercom
fyi the second example will fail pep8 checks: E701 multiple statements on one line so also non-pythonic... ;)Interference
Note that this and-shortcircuiting doesn't seem to work for assignments: <condition> and (strng = 'myString')Otilia
This is not foolproof if you have objects/dictionaries in the condition: x = object['attribute'] or None will throw a KeyError exception if there is no attribute key in object. The correct way will still be: x = object['attribute'] if 'attribute' in object else NoneContretemps
@hard_working_ant The "correct" way would be x = object.get('attribute') in that casePrinciple
@Principle : yes and further adding x = object.get('attribute', None) to default to NoneContretemps
@overflower dict.get already defaults to None.Principle
H
66

The reason the language doesn't allow you to use the syntax

variable = "something" if a_condition

without else is that, in the case where a_condition == False, variable is suddenly unknown. Maybe it could default to None, but Python requires that all variable assignments actually result in explicit assignments. This also applies to cases such as your function call, as the value passed to the function is evaluated just as the RHS of an assignment statement would be.

Similarly, all returns must actually return, even if they are conditional returns. Eg:

return variable if a_condition

is not allowed, but

return variable if a_condition else None

is allowed, since the second example is guaranteed to explicitly return something.

Hurtado answered 30/8, 2012 at 14:59 Comment(5)
But I wanted to use it like this: continue if i == 0 in a for loop.Caliper
Are you allowed to do else pass?Sergiosergipe
else pass doesn't work because the ternary expression should return a value that can be passed to return. pass is not a valid return value.Hurtado
I don't agree that this motivation is the real reason, given that if a_condition: variable = "something" and if a_condition: return variable are legal. So it is essentially an arbitrary syntactic choice of python.Otilia
What if I want to append something to a list if the condition is True? Like: mylist.append("hi") if append_smth == TrueLauricelaurie
G
16
if <condition>: myList.append('myString')

Otherwise, no. Why the need to put it on one line?

Note that the "ternary operator" is an operator. Like any operator, it must return something, so how can you have a ternary operator without the else clause? What is it supposed to return if the condition isn't true-like?

Gamester answered 30/8, 2012 at 14:57 Comment(0)
C
13

You are basically asking for do_thing() if <condition> else pass construct (which will throw SyntaxError, if ran). As I have discovered during research for (somewhat) similar question do_thing() if condition else None is as close as you can get (which is just another way to do <condition> and do_thing()). So, to summarize this idea and other answers, here are your options:

  • if <condition>: myList.append('myString') — seems to be the least 'hacky' (and thus preferred) way
  • <condition> and myList.append('myString')
  • myList.append('myString') if <condition> else None
Cosenza answered 10/7, 2018 at 9:30 Comment(1)
The least hacky way doesn't work. At least not on the REPL - one gets a syntax error. Seems like it's caused by preceding code on the same line. This needs to be executed on its own line instead. But even then user input is required to skip the ellipsis.Cystoscope
A
5

i’d just do this if i wanna add optional elements to a list based on a condition.

nums = [
        1,
        2,
        3 if <condition> else None,
        4,
       ]

# nums now contains values of `None`, so we delete all occurrences of `None`
nums.remove(None)

this just replaces the value with None if the condition is not met and then later, it just redefines the list without the None Values. this way they preserve their index if the condition is met

Attorn answered 3/11, 2020 at 21:50 Comment(1)
@TheClockTwister, replace last line with nums.remove(None)Unremitting
J
4

myList.extend(['myString'] if condition else []) would also work, though it's more work than the other solutions.

Johannejohannes answered 30/8, 2012 at 15:21 Comment(0)
C
1

You can do something like below. Note that None is never at any point appended to myList.

myList.append('myString') if <condition> else None

Also, Python should accept the one-liner below.

if <condition>: myList.append('myString')
Colvert answered 17/11, 2018 at 4:54 Comment(2)
False and 0 are not appended to the list. The line just needs to return something if the condition is not met. You can replace the 0 or False with None or an empty string, or anything you want, and it will not affect the list.Colvert
I think my thought at the time was in the case of a list comprehension. Deleted my comment, downvote wasn't me. I suppose a cleaner way would be to do myList.append('myString') if True else _ But if you're not assigning to anything, doesn't matter. I'm concerned about this person attempting a list-comprehension with similar logic though.Pyretic
L
0

For all those people asking: "Why would you want to do that?", there is one case where that syntax would be extremely useful.

Take this code:

class a:
    def calla(self):
        print("a")

class b:
    def callb(self):
        print("b")

def c(*args):

    class c(a if args[0], b if args[1]):
        def __init__(self, *args):
            self.calla = getattr(self, "calla", None)
            if callable(self.calla):
                self.calla()
                
            self.callb = getattr(self, "callb", None)
            if callable(self.callb):
                self.callb()
                
    return c(args[2:])

C = c(False, False)

The code is attempting to conditionally inherit class a and/or/nor b. The getattr and callable lines check whether the method from class a or b exists, i.e. whether the classes were sucessfully inherited. The factory function is used to define the conditions for inheritance that are given to class c. And lastly, there are the conditions inside the definition of class c's inheritance.

Unfortunately this code doesn't run as there must be an else after the if... It cannot be avoided using a one line if statement either as starting with "if" is invalid syntax.

You also cannot else inherit object like this:

class c(a if args[0] else object, b if args[1] else object):

This code could throw an error of inheriting "object" twice if neither a or b need to be inherited. ("else None" and "else c" dont work either ofcourse)

Therefore the way to avoid it is by creating throwaway classes for a and b with nothing inside but this looks terrible and seems wrong...

class a:
    def calla(self):
        print("a")

class b:
    def callb(self):
        print("b")

class empty:
    pass
class empty2:
    pass

def c(*args):
    
    class c(a if args[0] else empty, b if args[1] else empty2):
        def __init__(self, *args):
            self.calla = getattr(self, "calla", None)
            if callable(self.calla):
                self.calla()
                
            self.callb = getattr(self, "callb", None)
            if callable(self.callb):
                self.callb()
                
    return c(args[2:])

C = c(False, False)

So it would be useful to have the ternary operator not require else.

Loadstar answered 13/10, 2023 at 20:9 Comment(0)
V
-1
def map_key_vs_values(db, key_name, val_name):
    _map = {}
    for item in db:
        p = item[key_name]
        a = item[val_name]

        try: a not in _map[p] and _map[p].append(a)
        except: _map[p] = [a]
        
    return _map
Valenevalenka answered 12/8, 2022 at 11:9 Comment(1)
This answer was reviewed in the Low Quality Queue. Here are some guidelines for How do I write a good answer?. Code only answers are not considered good answers, and are likely to be downvoted and/or deleted because they are less useful to a community of learners. It's only obvious to you. Explain what it does, and how it's different / better than existing answers. From ReviewUle

© 2022 - 2024 — McMap. All rights reserved.