Filtering stocks by dividend yield I have a dict of stock tickers (KEY) and a data point (VALUE; e.g. dividend yield).

I want to know which tickers have a value between a specified min and max value.

Given

data = {'KO': 1, 'WMT': 0, 'IBM': 3, 'PG': 2}
gte = 1
lte = 2


Func1

def func1(data, gte=None, lte=None):
    data_set = set(data)
    gte_set = set(filter(lambda k: data[k] &gt;= gte, data)) if gte else data_set
    lte_set = set(filter(lambda k: data[k] &lt;= lte, data)) if lte else data_set
    return data_set.intersection(gte_set, lte_set)


Func2

import operator
def func2(data, gte=None, lte=None):
    if gte is None and lte is None:
        return data
    #without dict data gets modified
    _data = dict(data)
    for k, v in data.items():
        if gte and operator.lt(v, gte):
                del _data[k]
        if lte and operator.gt(v, lte):
                del _data[k]
    return set(_data)


Func3

def func3(
    data,
    gte=float('-inf'),
    lte=float('inf')
):
    return set(filter(
        lambda k: gte &lt;= data[k] &lt;= lte,
        data
    ))


Example

func1(data, gte=gte, lte=lte) == {'PG', 'KO'}
func2(data, gte=gte, lte=lte) == {'PG', 'KO'}
func3(data, gte=gte, lte=lte) == {'PG', 'KO'}
First of all, I would rename the bounds to be more meaningful, from gte to min_value, and from lte to max_value.



Function 1 comes close to being a good idea, but could be implemented more smartly.  The special treatment of any falsy value (notably 0) to skip filtering is probably a bad idea.  The set intersection operation should be avoidable.

def func1_improved(data, min_value=None, max_value=None):
    if min_value is not None:
        data = {(k, v) for k, v in data if v &gt;= min_value}
    if max_value is not None:
        data = {(k, v) for k, v in data if v &lt;= max_value}
    return data.keys()




Function 2 is not equivalent to the other two: it returns a dictionary rather than a key set.  It suffers from the problem that it sometimes returns the original dictionary, and sometimes returns a copy.  It also treats 0 as a special parameter value.

def func2_improved(data, min_value=None, max_value=None):
    data = dict(data)    # Make a copy
    for k, v in data.items():
        if min_value is not None and v &lt;= min_value:
                del data[k]
        if max_value is not None and v &gt;= max_value:
                del data[k]
    return data.keys()




Function 3 is quite good, especially when formatted as a one-liner using a set comprehension:

def func3_improved(data, min_value=float('-inf'), max_value=float('inf')):
    return set(k for (k, v) in data.items() if min_value &lt;= v &lt;= max_value)


That is my recommendation.  You could eliminate some comparisons by making special cases for when min_value or max_value is missing, but I'd prefer not to ruin the elegance of the one-line solution.