Andrius
Andrius

Reputation: 21188

Python - adding conditions in if only they are specified (domain)?

I'm writing a method that will filter records by called conditions. What I mean, is it will check if default conditions are met for any record and then if specified, checks if additional conditions needs to be met for those records. But if those conditions are not specified, it will just ignore it.

So I could write it like this:

def test_conditions(a, b=None)
    #b - this one is additional condition that might be specified or not
    if a > 5 and (b > 10 if b else True):
        print 'record passed'

It is working I need to, but I don't think it looks very elegant and I will need to use more than one additional condition that might or might not be specified when method is called and then such method might not look good.

So maybe there is some practice in Python how to best solve such problems (I mean best way to handle those additional conditions when you don't know if you will need to check it or not before method is called)?

I was asked to provide real code I was writing, so here it is:

def countEmergency(self, card_ids, duration=None):
    cnt = 0
    for card in card_ids:
        if card.emergency_level == 'first' and (card.duration > duration if duration else True):
            cnt += 1
    return cnt

Upvotes: 1

Views: 262

Answers (1)

jonrsharpe
jonrsharpe

Reputation: 122150

Rather than print I would return either True or False, then you can check each item individually. Assuming that you're testing the attributes on some object (but this can be adapted to whatever you need):

def test_conditions(obj, a=5, b=None):
    if obj.a <= a:
        return False
    if b is not None and obj.b <= b:
        return False
    return True

Alternatively, use **kwargs to take arbitrary keyword arguments:

def test_conditions(obj, a=5, **kwargs):
    if obj.a <= a:
        return False
    for k, v in kwargs.items():
        if getattr(obj, k) <= v:
            return False
    return True

or

def test_conditions(obj, **kwargs):
    if obj.a <= kwargs.get('a', 5):
        return False
    for k, v in kwargs.items():
        if getattr(obj, k) <= v:
            return False
    return True

Given what you're actually doing, use continue instead of returning:

def countEmergency(self, card_ids, duration=None):
    cnt = 0
    for card in card_ids:
        if card.emergency_level != 'first':
            continue
        if duration is not None and card.duration <= duration:
            continue
        cnt += 1
    return cnt

Alternatively, refactor to use a function as above, that takes one card, and use filter to get a list of valid elements from card_ids.

from functools import partial

class WhateverThisIs(object):

    ...

    @staticmethod
    def validate_card(card, duration=None):
        if card.emergency_level != 'first':
            return False
        if duration is not None and card.duration <= duration:
            return False
        return True

    def countEmergency(self, card_ids, duration=None):
        return len(filter(partial(self.validate_card, duration=duration), card_ids))

This uses functools.partial to create an appropriate filtering function. Note that your countEmergency doesn't use any instance or class attributes or methods - you should review whether it needs to be an instance method.

Upvotes: 3

Related Questions