Does Python have an "or equals" function like ||= in Ruby? - python

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.

Jon-Eric's answer's is good for dicts, but the title seeks a general equivalent to ruby's ||= operator.
A common way to do something like ||= in Python is
x = x or new_value

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).

dict has setdefault().
So if request.session is a dict:
request.session.setdefault('thing_for_purpose', 5)

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 ...

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

Related

how to get the variable from an any() function

I am looking to get the list_select variable that meets the criteria and do an append in the next line.
How can I make this available in the list_select.append(dupe) line?
if any(list_select in dupe for list_select in pattern_dict):
list_select.append(dupe)
You can't with any, which just returns a boolean. Use a generator expression instead:
gen = (x for x in pattern_dict if x in dupe)
list_select = next(gen, None)
if list_select is not None:
...
The any function is supposed to return a boolean, so if you want the slightly different behaviour of returning the first match, write a function with that behaviour:
def first(seq):
return next(iter(seq), None)
Usage:
>>> first( i for i in range(10) if i**2 > 10 )
4
>>> first( c for c in 'Hello, world!' if c.islower() )
'e'
>>> first( i for i in range(10) if i == 100 ) is None
True
To use in your example, you would write something like:
list_select = first( x for x in pattern_dict if x in dupe )
if list_select is not None:
list_select.append(dupe)
If you are using Python 3.8 or later, the dreaded "walrus" operator allows for a more direct solution:
if any((list_select := x) in dupe for x in pattern_dict):
list_select.append(dupe)
This situation happens to be one of the motivating examples for introducing the walrus operator.

Python: general iterator or pure function for testing any condition across list

I would like to have a function AllTrue that takes three arguments:
List: a list of values
Function: a function to apply to all values
Condition: something to test against the function's output
and return a boolean of whether or not all values in the list match the criteria.
I can get this to work for basic conditions as follows:
def AllTrue(List, Function = "Boolean", Condition = True):
flag = True
condition = Condition
if Function == "Boolean"
for element in List:
if element != condition:
flag = False
break
else:
Map = map(Function, List)
for m in Map:
if m != condition:
flag = False
break
return flag
Since python doesn't have function meant for explicitly returning if something is True, I just make the default "Boolean". One could clean this up by defining TrueQ to return True if an element is True and then just mapping TrueQ on the List.
The else handles queries like:
l = [[0,1], [2,3,4,5], [6,7], [8,9],[10]]
AllTrue(l, len, 2)
#False
testing if all elements in the list are of length 2. However, it can't handle more complex conditions like >/< or compound conditions like len > 2 and element[0] == 15
How can one do this?
Cleaned up version
def TrueQ(item):
return item == True
def AllTrue(List, Function = TrueQ, Condition = True):
flag = True
condition = Condition
Map = map(Function, List)
for m in Map:
if m != condition:
flag = False
break
return flag
and then just call AllTrue(List,TrueQ)
Python already has built-in the machinery you are trying to build. For example to check if all numbers in a list are even the code could be:
if all(x%2==0 for x in L):
...
if you want to check that all values are "truthy" the code is even simpler:
if all(L):
...
Note that in the first version the code is also "short-circuited", in other words the evaluation stops as soon as the result is known. In:
if all(price(x) > 100 for x in stocks):
...
the function price will be called until the first stock is found with a lower or equal price value. At that point the search will stop because the result is known to be False.
To check that all lengths are 2 in the list L the code is simply:
if all(len(x) == 2 for x in L):
...
i.e. more or less a literal translation of the request. No need to write a function for that.
If this kind of test is a "filter" that you want to pass as a parameter to another function then a lambda may turn out useful:
def search_DB(test):
for record in database:
if test(record):
result.append(record)
...
search_DB(lambda rec: all(len(x) == 2 for x in rec.strings))
I want a function that takes a list, a function, and a condition, and tells me if every element in the list matches the condition. i.e. foo(List, Len, >2)
In Python >2 is written lambda x : x>2.
There is (unfortunately) no metaprogramming facility in Python that would allow to write just >2 or things like ·>2 except using a string literal evaluation with eval and you don't want to do that. Even the standard Python library tried going down that path (see namedtuple implementation in collections) but it's really ugly.
I'm not saying that writing >2 would be a good idea, but that it would be nice to have a way to do that in case it was a good idea. Unfortunately to have decent metaprogramming abilities you need a homoiconic language representing code as data and therefore you would be programming in Lisp or another meta-language, not Python (programming in Lisp would indeed be a good idea, but for reasons unknown to me that approach is still unpopular).
Given that, the function foo to be called like
foo(L, len, lambda x : x > 2)
is just
def foo(L, f=lambda x : x, condition=lambda x: x):
return all(condition(f(x)) for x in L)
but no Python programmer would write such a function, because the original call to foo is actually more code and less clear than inlining it with:
all(len(x) > 2 for x in L)
and requires you to also learn about this thing foo (that does what all and a generator expression would do, just slower, with more code and more obfuscated).
You are reinventing the wheel. Just use something like this:
>>> l = [[0,1], [2,3,4,5], [6,7], [8,9],[10]]
>>> def all_true(iterable, f, condition):
... return all(condition(f(e)) for e in iterable)
...
>>> def cond(x): return x == 2
...
>>> all_true(l, len, cond)
False
You can define a different function to check a different condition:
>>> def cond(x): return x >= 1
...
>>> all_true(l, len, b)
True
>>>
And really, having your own function that does this seems like overkill. For example, to deal with your "complex condition" you could simply do something like:
>>> l = [[0,2],[0,1,2],[0,1,3,4]]
>>> all(len(sub) > 2 and sub[0] == 5 for sub in l)
False
>>> all(len(sub) > 1 and sub[0] == 0 for sub in l)
True
>>>
I think the ideal solution in this case may be:
def AllTrue(List, Test = lambda x:x):
all(Test(x) for x in List)
This thereby allows complex queries like:
l = [[0, 1], [1, 2, 3], [2, 5]]
AllTrue(l, lambda x: len(x) > 2 and x[0] == 1)
To adhere to Juanpa's suggestion, here it is in python naming conventions and an extension of what I posted in the question now with the ability to handle simple conditions like x > value.
from operator import *
all_true(a_list, a_function, an_operator, a_value):
a_map = map(a_function, a_list)
return all( an_operator(m, a_value) for m in a_map)
l = [[0,2],[0,1,2],[0,1,3,4]]
all_true(l, len, gt, 2)
#True
Note: this works for single conditions, but not for complex conditions like
len > 2 and element[0] == 5

How can I return false if more than one number while ignoring "0"'s?

This is a function in a greater a program that solves a sudoku puzzle. At this point, I would like the function to return false if there is more then 1 occurrence of a number unless the number is zero. What do am I missing to achieve this?
L is a list of numbers
l =[1,0,0,2,3,0,0,8,0]
def alldifferent1D(l):
for i in range(len(l)):
if l.count(l[i])>1 and l[i] != 0: #does this do it?
return False
return True
Assuming the list is length 9, you can ignore the inefficiency of using count here (Using a helper datastructure - Counter etc probably takes longer than running .count() a few times). You can write the expression to say they are all different more naturally as:
def alldifferent1D(L):
return all(L.count(x) <= 1 for x in L if x != 0)
This also saves calling count() for all the 0's
>>> from collections import counter
>>> def all_different(xs):
... return len(set(Counter(filter(None, xs)).values()) - set([1])) == 0
Tests:
>>> all_different([])
True
>>> all_different([0,0,0])
True
>>> all_different([0,0,1,2,3])
True
>>> all_different([1])
True
>>> all_different([1,2])
True
>>> all_different([0,2,0,1,2,3])
False
>>> all_different([2,2])
False
>>> all_different([1,2,3,2,2,3])
False
So we can break this down into two problems:
Getting rid of the zeros, since we don't care about them.
Checking if there are any duplicate numbers.
Striping the zeros is easy enough:
filter(lambda a: a != 0, x)
And we can check for differences in a set (which has only one of each element) and a list
if len(x) == len(set(x)):
return True
return False
Making these into functions we have:
def remove_zeros(x):
return filter(lambda a: a != 0, x)
def duplicates(x):
if len(x) == len(set(x)):
return True
return False
def alldifferent1D(x):
return duplicates(remove_zeros(x))
One way to avoid searching for every entry in every position is to:
flags = (len(l)+1)*[False];
for cell in l:
if cell>0:
if flags[cell]:
return False
flags[cell] = True
return True
The flags list has a True at index k if the value k has been seen before in the list.
I'm sure you could speed this up with list comprehension and an all() or any() test, but this worked well enough for me.
PS: The first intro didn't survive my edit, but this is from a Sudoku solver I wrote years ago. (Python 2.4 or 2.5 iirc)

Multiple inequality (a < b < c...) with potentially missing value

I would like to test multiple inequalities at once, i.e.
if (a < b < c < ...)
which is fine when all the values are present. However sometimes the numeric value of one or more of the variables to be compared may be missing/unknown; the correct behaviour in my context is to assume the associated inequality is satisfied. Let's say I assign the special value None when the value is unknown: the behaviour I want from the < operator (or an alternative) is:
>>> a = 1; b = 2; c = 3
>>> a < b < c # this works fine, obviously
True
>>> b = None
>>> a < b < c # would like this to return True
False
So I want to get True if one variable is truly smaller than the other, or if one variable is missing (takes any particular pre-decided non-numerical value), or if both variables are missing, and I want to be able to string the comparisons together one go i.e. a < b < c < ...
I would also like to do this with <= as well as <.
Thanks
You want to test if your sequence – bar the undefined values – is in ascending order:
import operator
def isAscending(strictly, *seq):
cmp_op = operator.lt if strictly else operator.le
seq = [e for e in seq if e is not None]
return all(cmp_op(a, b) for a, b in zip(seq, seq[1:]))
a, b, c = 1, None, 2
print isAscending(True, a, b, c) # strictly ascending ?
Edited for spelling, and to use comparison operators as suggested.
This looks like you are actually trying to test if your values are unique and in sorted order which could be replaced by something like:
>>> def my_test(l):
>>> filt_l = [v for v in l if not v is None]
>>> return (sorted(filt_l) == filt_l) and (len(filt_l) == len(set(filt_l)))
>>> my_test([1,2,3])
True
>>> my_test([1,None,3])
True
>>> my_test([1,4,3])
False
>>> my_test([1,1,3])
False
Edit: including timings it seems that the function suggested by sebdelsol is even faster
>>> %timeit isAscending([int(1000*random.random()) for i in xrange(10000)])
100 loops, best of 3: 3.44 ms per loop
>>> %timeit my_test([int(1000*random.random()) for i in xrange(10000)])
100 loops, best of 3: 5.67 ms per loop
You could create your own type overloading comparison methods (as in this question: python overloading operators)
E.g.
class N(object):
def __init__(self, value):
self.value = value
def __lt__(self, other):
return (self.value is None or self.value < other.value)
...
a = N(1); b = N(None); c = N(3)
print a < b < c
If you have your values in a list ([a, b, c]), then you can filter the None values from it, pair them up using zip(), apply the operator to all the pairs and see if all them hold.
In code:
import operator # For operator.lt, which is < ("less than")
def mass_comparify(op, *args):
return all(op(a, b) for a, b in zip(args, args[1:])
if a is not None and b is not None)
print(mass_comparify(operator.lt, 1, None, 3)) # Prints True because 1 < 3
I don't think you have a better option than to define a comparison function which does the comparison as you want, and then write the inequalities as
comp(a,b) and comp(b,c) and ...
I don't know if it fits perfectly, but you can use reduce:
>>> import operator
>>> reduce(operator.__lt__, [1, None, 3])
True
>>> reduce(operator.__lt__, [1, None, 0])
False
or, much more solid, as it explicitly ignores None values:
>>> import operator
>>> reduce(operator.__lt__, filter(None, [1, None, 3]))
True
>>> reduce(operator.__lt__, filter(None, [1, None, 0]))
False

does adding element to a set return true if it is successful in python?

To my understanding, adding elements to a set does not return anything.
eg:
>>> s=set([1,2,3])
>>> s.add(1)
>>> b = s.add(1)
>>> b
>>> b = s.add(5)
>>> b
>>> print b
None
>>> b = s.add(5)
>>> print b
None
but I am confused how this function to remove duplicates of a list while preserving order works:
def f7(seq):
seen = set()
seen_add = seen.add
return [ x for x in seq if x not in seen and not seen_add(x)]
not seen_add(x) return true always irrespective of if I add duplicates or not.
The only real check is if x not in seen, since if that fails, it will not perform not see_add(x).
Is my understanding here or am I missing something?
however, here, not seen_add(1) return true if I add duplicate or a unique element.
This is puzzling, so is the not here just do an empty check? but seen_add() is not returning a set to do an empty check. and why seen_add(2) does not return anything but not seen_add(1) returns the boolean True
>>> seen=set()
>>> seen_add=seen.add
>>> seen_add(1)
>>> seen
set([1])
>>> seen.add(2)
>>> seen
set([1, 2])
>>> not seen_add(1)
True
>>> not seen_add(4)
True
As you have discovered, seen_add(x) always returns None.
Now, not None is True:
>>> not None
True
This is explained in the documentation:
5.1. Truth Value Testing
Any object can be tested for truth value, for use in an if or while condition or as operand of the Boolean operations below. The following values are considered false:
None
False
...
Since not seen_add(x) is always True, it has no effect on the result of the if. It is only used for the side effect of adding x to seen.
Put another way, the following:
seen_add = seen.add
return [ x for x in seq if x not in seen and not seen_add(x)]
is a shorter (and likely more performant) way to write:
result = []
for x in seq:
if x not in seen:
result.append(x)
seen.add(x)
return result
As said by the others, not seen_add(x) always evaluates to True, so it is used only as a hack to add the addition statement into the list comprehension. Since the logic value of x and True is equal to x, this does not change the behavior of the if.
Another thing that this does, due to the short-circuit behavior of and, is that the right-hand argument to and is only executed if the left-hand argument is True. So the call to seen.add() is only done when the element is not yet in the set.
The equivalent python code would be:
def f7(seq):
seen = set()
result = []
for x in seq:
if x not in seen:
result.append(x)
seen.add(x)
return result
The solution with the list comprehension might be faster, but it is too cryptic to my taste.
This is because not None is True.
maybe this can help you
def add_ok(set, value):
return len(set) != (set.add(value), len(set))[1]
print(add_ok(a, 1))
print(add_ok(a, 1))

Categories