117

If not, what is the best way to do this?

Right now I'm doing (for a django project):

if not 'thing_for_purpose' in request.session: request.session['thing_for_purpose'] = 5 

but its pretty awkward. In Ruby it would be:

request.session['thing_for_purpose'] ||= 5 

which is much nicer.

5
  • 25
    Note that these two bits of code are actually very different: the Python version sets it to 5 if it's not in the dict at all, where the Ruby version also sets it to 5 if it's set to any false value. Commented Oct 14, 2010 at 1:42
  • 2
    @Glenn, not very different, but quite different. Since an uninitialized hash value returns nil (false in boolean context) this idiom is often used exactly for the purpose sean has used it. The idiom only works though if, of course, nil and false are not legitimate values in the hash (which is very often true, so the idiom is fine) Commented Oct 14, 2010 at 8:34
  • 9
    @banister: I don't know where one might draw the line between "very" and "quite", but the point is these aren't equivalent statements, and it's important to understand the difference. (False is very often a valid value in a hash, which will bite you when you want to set a default for a boolean field to true; it's a significant shortcoming of Ruby's idiom.) Commented Oct 14, 2010 at 9:47
  • Yes the Python example is more akin to defined-or, like //= which you find in perl. I think the ||= also originates from Perl. Commented Oct 14, 2010 at 13:51
  • Ah good points, all. I hadn't noticed this difference. Commented Oct 14, 2010 at 20:44

5 Answers 5

233

Jon-Eric's answer's is good for dicts, but the title seeks a general equivalent to 's ||= operator.

A common way to do something like ||= in Python is

x = x or new_value 
Sign up to request clarification or add additional context in comments.

7 Comments

This answer is likely what the folks coming here from a Google search are looking for.
Note that unlike in Ruby, this code will only work if x has been previously defined (for example set to None in a class initialization method).
True, this doesn't behave exactly like Ruby's ||=. Another thing to watch out for is the 'truthiness' of x. If x==0 in Python, you'll get new_value since 0 is considered false, but in Ruby you'll get 0.
Is this as efficient as ||=? ||= doesn't do an assignment unless its null, but this always does an assignment. Is the python compiler is cleverer than I think it is?
this DOESN'T work with the specific question the OP asked. x = request.session['thing_for_purpose'] or 'my default' will raise a KeyError if 'thing_for_purpose' is not defined in the dict. The other answers in this thread with "get" on a dict are IMHO better answers
|
26

Precise answer: No. Python does not have a single built-in operator op that can translate x = x or y into x op y.

But, it almost does. The bitwise or-equals operator (|=) will function as described above if both operands are being treated as booleans, with a caveat. (What's the caveat? Answer is below of course.)

First, the basic demonstration of functionality:

x = True x Out[141]: True x |= True x Out[142]: True x |= False x Out[143]: True x &= False x Out[144]: False x &= True x Out[145]: False x |= False x Out[146]: False x |= True x Out[147]: True 

The caveat is due python not being strictly-typed, and thus even if the values are being treated as booleans in an expression they will not be short-circuited if given to a bitwise operator. For example, suppose we had a boolean function which clears a list and returns True iff there were elements deleted:

def my_clear_list(lst): if not lst: return False else: del lst[:] return True 

Now we can see the short-circuited behavior as so:

x = True lst = [1, 2, 3] x = x or my_clear_list(lst) print(x, lst) Output: True [1, 2, 3] 

However, switching the or to a bitwise or (|) removes the short-circuit, so the function my_clear_list executes.

x = True lst = [1, 2, 3] x = x | my_clear_list(lst) print(x, lst) Output: True [] 

Above, x = x | my_clear_list(lst) is equivalent to x |= my_clear_list(lst).

Comments

20

dict has setdefault().

So if request.session is a dict:

request.session.setdefault('thing_for_purpose', 5) 

2 Comments

Vaguely similar but in no way the equivalent of Ruby's ||=.
It's significantly different in that the default expression is evaluated even if the key is already set. There is no way to solve the original question without either evaluating the default value, or accessing the key more than once, or both.
10

Setting a default makes sense if you're doing it in a middleware or something, but if you need a default value in the context of one request:

request.session.get('thing_for_purpose', 5) # gets a default 

bonus: here's how to really do an ||= in Python.

def test_function(self, d=None): 'a simple test function' d = d or {} # ... do things with d and return ... 

Comments

0

In general, you can use dict[key] = dict.get(key, 0) + val.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.