-2

Is there a pythonic way to do the following? In a world with 2 words word1 and word2, I want to return the opposite one, preferably with a one-liner

I'm currently doing the following and works but doesn't feel right:

def get_other_word(word):
    if word == word1:
        return word2
    else:
        return word1

I was thinking something like this but it throws an error:

def get_other_word(word):
    word_choices = [word1, word2]
    return word_choices.remove(word)[0]
s g
  • 4,057
  • 6
  • 39
  • 67
  • I think your confusion comes from thinging "shorter code" is more pythonic, the Zen of Python basically encourages clean, clear, well written code. A one-liner is rarely any of those things. –  May 01 '16 at 07:10
  • Exactly, and I commented on your answer. I was thinking of the c++ ternary operator and simply figured there must be something similar but it seems that there isn't. – s g May 01 '16 at 07:15
  • Ternaries are garbage anyway. Less lines doesn't always translate to better or faster code. The machine will optimize it regardless of how pretty it is, and a simple `if-then-else` despite being on 4 lines in python will translate to much quicker calls than a lambda/dict/array approach. –  May 01 '16 at 07:18
  • "Ternaries are garbage anyway" - primarily opinion-based, citation needed, etc. :P – TigerhawkT3 May 01 '16 at 07:39

4 Answers4

2

From PEP20 - The Zen of Python

Explicit is better than implicit.
Simple is better than complex.

What you are doing in your first code block is fine. There are certainly "cleverer" ways to do what you are asking, but they will be opaque and difficult to read.


To add further remarks on why using an if-then-else block is the best approach, this doesn't require any additional object creation on the part of the interpreter and will execute quite quickly.

In comparison, making and calling an anonymous lambda, or stuffing everything in a dict or array to extract from just for the sake of putting everything on one line will actually make the code slower. Executed once, this won't be a big deal, but if this code is called thousands of times a second it might add up.

Complexity isn't your friend, and making code readable for people is just as important as making code readable for machines.

  • He's explicitly asking for a one-liner. – Aiman Al-Eryani May 01 '16 at 07:08
  • He's explicitly asking for pythonic, one liners are great for showing off, but are usually terrible and unreadable code. –  May 01 '16 at 07:09
  • "Pythonic" sounds subjective to me. A lambda function is clear and perhaps "pythonic" enough. – Aiman Al-Eryani May 01 '16 at 07:11
  • But Pythonic isn't "subjective" there is a standardised document on "The Zen of Python" which I linked, which is well regarded when it comes to establishing if something is "Pythonic' or not. –  May 01 '16 at 07:12
  • Okay, okay guys let me set things straight before we get into an unnecessary flame war - I made the *incorrect* assumption that there would be an ideal, python, simple way to achieve my goal. So, it turns out that they answers vary because my question isn't perfect. – s g May 01 '16 at 07:14
1

There is nothing wrong or "unpythonic" about your first version, the reason your second version doesn't work is because word_choices.remove(word) is an in-place operation (it modifies the list and returns None):

>>> help([].remove)
Help on built-in function remove:

remove(...)
    L.remove(value) -- remove first occurrence of value.
    Raises ValueError if the value is not present.

That means, that it will not return anything, and you are in-effect doing None[0] which is not a valid operation.

You could have fixed your second version like this:

def get_other_word(word):
    word_choices = [word1, word2]
    word_choices.remove(word)
    return word_choices[0]

However, your first version with the two if statements is more legible and if you were to drop down to the nitty gritty, slightly better as it doesn't build a list.

Burhan Khalid
  • 152,028
  • 17
  • 215
  • 255
-1

For your second code block:

def get_other_word(word):
    word_choices = [word1, word2]
    return word_choices.remove(word)[0]

list.remove modifies a list in-place and returns None, so you would have to first save a reference, best expressed as follows:

def get_other_word(word):
    word_choices = [word1, word2]
    word_choices.remove(word)
    return word_choices[0]

You could also use the fact that False corresponds to 0 and True corresponds to 1, and index the list for the desired value, which does happen to be a one-liner:

def get_other_word(word):
    return [word1, word2][word == word1]

Result:

>>> word1 = 'apple'
>>> word2 = 'banana'
>>> get_other_word('apple')
'banana'
TigerhawkT3
  • 44,764
  • 6
  • 48
  • 82
-2

You can use the ternary operator for a one-liner:

return word2 if word == word1 else word1
Idos
  • 14,036
  • 13
  • 48
  • 65
  • I guess people just like dumping downvotes like that. I'll still keep this here since it answers the question as stated and I don't see anything wrong or unreadable about it at all – Idos May 01 '16 at 07:12
  • Ternaries aren't really used in Python, partly because they overload logic and assignment, and partly because their syntax in Python is unreadable. Where possible don't use them. The question ask for "pythonic" and they aren't, hence downvotes. –  May 01 '16 at 07:14
  • [seems like it's a really big thing in python](http://stackoverflow.com/questions/394809/does-python-have-a-ternary-conditional-operator) – Idos May 01 '16 at 07:18
  • 2
    @LegoStormtroopr Just get off your high horse. "aren't really used". The above especially is made to be a syntactic sugar to increase readability. – Aiman Al-Eryani May 01 '16 at 07:24