9

Possible Duplicate:
Python conditional assignment operator

Apologies for such a simple question, but googling ||= isn't very helpful ;)

Is there an equivalent in Python to the ||= statement that's present in Ruby and Perl?

For example:

foo = "hey"
foo ||= "what"    # assign foo if it's undefined
# foo is still "hey"

bar ||= "yeah"
# bar is "yeah"

Also what's the general term for something like this? Conditional assignment was my first guess but the Wikipedia page isn't quite what I had in mind.

Community
  • 1
  • 1
benui
  • 6,440
  • 5
  • 34
  • 49
  • Yes it's a duplicate it seems. Thanks Adam! – benui Jan 30 '12 at 05:23
  • 3
    `$foo ||= "what"` will reassign `$foo` if it is already *false*. Of course `undef` is false, but then so is zero and the null string. To reassign only an undefined value use slashes instead, like `$foo //= "what"` – Borodin Jan 30 '12 at 05:28
  • Note that `//` (and its extension, `//=`) was added in perl 5.10. If you're still stuck on 5.8.x, you won't be able to use `//=` (and you should seriously consider upgrading if at all possible). – Dave Sherohman Jan 30 '12 at 14:40

3 Answers3

11

A tad bit more verbose, but the easiest is

foo = "hey"
foo = foo or "what"
#foo is still "hey"

bar = None
bar = bar or "yeah"
#bar is "yeah"

You can also use the ternary operator

bar = None
bar = bar if bar else "yeah"

However, if I understand you, ||= assigns variables that weren't previously defined, without complaint? I had no idea.

To do that in the local scope, this ugly duckling could work

bar = locals()['bar'] if 'bar' in locals() else 'yeah'

EDIT:

Just saw the duplicate, and it has plenty of solutions as well :) For those too lazy to look, they also include a nicer variant on my last one

foo = foo if 'foo' in locals() else 'hey'

but this won't work for undefined variables, only falsy values will be replaced and undefined will raise a NameError. This next one will, OTOH, ONLY work for undefined and always keep the same preexisting falsy value, which as @Borodin says is like //= in Perl

foo = locals().get('foo','hey')

and, of course, someone used an exception :(

try:
   v
except NameError:
   v = 'bla bla'
Matt Luongo
  • 14,371
  • 6
  • 53
  • 64
  • no, `||=` will reassign a variable if it is `false`. See my comment on the question. Why would you expect a complaint? – Borodin Jan 30 '12 at 05:30
  • Thanks for the reply, at first I didn't notice the nuanced difference. the `bar = None` is required for it to work, otherwise it throws a `NameError`. For identical behaviour you need your `locals()['bar']` snippet. Thanks again :) – benui Jan 30 '12 at 05:31
  • @borodin I'm not a Rubyist, I've only used it for silly stuff like Chef. I just meant that I didn't know that `undef` in Ruby was falsey, since that's not the case in Python (undefined references throw exceptions). Regardless, thanks for your comments, I learned something :) – Matt Luongo Jan 30 '12 at 05:35
3

You'll hardly ever have undefined variables in Python, and when you do it usually means you have made a mistake. However, conveniently, most of the sorts of values that are commonly used as defaults (empty containers, zero-length strings, zero, and None) are "falsy" in Python, so you will sometimes see stuff like this that takes advantage of how Boolean operators work in Python:

name = name or "Guido"   # if name is empty, set it to "Guido"
numb = numb or 42        # if numb is zero, set it to 42

The reason this works is that Python stops evaluating or if the first argument is "truthy," which is called short-circuiting, and in either case returns the actual argument, rather than merely True or False, as its result. So if name is "Jim" then "Jim" or "Guido" evaluates to "Jim" because "Jim" is a non-zero-length string and therefore "truthy."

Of course, this doesn't work so well when you don't know the type of the value you're dealing with and/or a "falsy" value is a valid value. However, it works pretty well with things like configuration files and raw_input() where a missing value will return an empty string:

name = raw_input("What is your name? ") or "Guido"

Another common idiom is used when dealing with items in a dictionary. The dictionary class's get() method lets you specify a default value to be used if the variable isn't in the dictionary.

name = values.get("name", "Guido")

This can be used when your function has been passed keyword arguments using the **kwargs convention. You could also use it with variables, as the globals() and locals() functions return, respectively, all global or local variables currently in scope as a dictionary:

name = locals().get("name", "Guido")

However, as I said, you will rarely ever have actually undefined variables in Python. In cases like Web frameworks, you'll be passed query string arguments as a dictionary, for example, and can use the dictionary's .get() method on it.

In the rare case where a name actually does not exist (for example, your configuration file is in Python syntax and you're importing it as a Python module rather than parsing it, and you want users to be able to omit some values... or something equally wacky) then you can use getattr(), which (like dictionary's .get()) accepts a default value:

import config
name = getattr(config, "name", "Guido")    # rather than just name.config

Or just let it throw a NameError and catch it.

kindall
  • 178,883
  • 35
  • 278
  • 309
1

Not sure about the name. The best I could come up with is:

>>> a = 'foo'
>>> a = 'a' in locals() and a or 'what'
>>> a
'foo'
>>> b = 'b' in locals() and b or 'yeah'
>>> b
'yeah'
GuillaumeDufay
  • 1,118
  • 9
  • 13