简体   繁体   中英

What's the most pythonic way to write this filtered list comprehension?

Maybe i'm just up too late.

I have an object that is a thin wrapper around a dictionary. It will pretend to have a property for any key in the dictionary, and return None if a nonexistent key is referenced.

I want to get back only the unique, "truthy" values for three possible keys. (not None). the object may not have one or more of the keys. Or, it may have the same value in two or three of the keys.

This code does what I want:

set(getattr(obj, field) for field in ['field1', 'field2', 'field3'] if getattr(obj, field))

I just don't like the look of repeating getattr() twice. I feel like i'm overlooking an obviously better way to do this.

You could filter out the None values afterwards:

set(filter(bool, [getattr(obj, field) for field in ['field1', 'field2', 'field3']]))

Or you could just forget about the object. This is probably the way I would do it:

a_dict = {'key1': 1, 'key2': 2, 'key3missing': 3}

print set([a_dict[key] for key in ['key1','key2','key3'] if key in a_dict])

# prints: set([1, 2])

If your thin wrapper returns default None :

s = set(getattr(obj, field) for field in ['field1', 'field2', 'field3']) - {None}

another possibility:

s = set(filter(None, (getattr(obj, field) for field in ['field1', 'field2', 'field3']))

another take on using the getattr default:

set(x for x in getattr(obj, field, None) for field in ['field1', 'field2', 'field3'] if x)

edit:

here is to show you the logic behind this function - and to show why i think it might be better than some of the other solutions (i could be wrong - but hey - life is all about learning from mistakes)

obj = Your_Object
fields = ['field1', 'field2', 'field3']

def get_set(obj, fields):
    result = []
    for field in fields:
        x = getattr(obj, field, None)
        if x:
            result.append(x)
    return set(result)

as you can see, there is only 1 loop, and getattr() is only called once for each field. and only "truthy" values are added to the result. i think this is a bit more efficient than getting all the results, and then removing the "non-truthy" values later. but please correct me if i am wrong. cheers.

Something like:

from operator import attrgetter

class DictWrapper(object):
    def __init__(self, d):
        self.d = d

    def __repr__(self):
        return repr(self.d)

    def __getattr__(self, name):
        return self.d.get(name)

    def truthy(self, *keys):
        values = attrgetter(*keys)(self)
        if not isinstance(values, tuple):
            values = (values,)
        return filter(None, set(values))

dw = DictWrapper({'x': 3, 'y': None, 'z': 'zebra'})
dw.truthy('x', 'y' ,'z', 'bob')
# [3, 'zebra']
dw.truthy('x')
# [3]
dw.truthy('y')
# []

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM