John Magistr
John Magistr

Reputation: 921

Feedback on implementation of function which compares integer signs in Python

I've made a small function which, given a tuple, compares if all elements in this tuple is of the same sign.

E.g., tuple = [-1, -4, -6, -8] is good, while [-1, -4, 12, -8] is bad. I am not sure I've made the smartest implementation, so I know this is the place to ask.

def check_consistent_categories(queryset):
    try:
        first_item = queryset[0].amount

        if first_item < 0:
            for item in queryset:
                if item > 0:
                    return False
            return True
        else:
            for item in queryset:
                if item < 0:
                    return False
            return True
    except:
        return False

Upvotes: 3

Views: 282

Answers (9)

John Machin
John Machin

Reputation: 82992

def all_same_sign(iterable):
    # Works with any iterable producing any items that can be compared to zero.
    # Iterates through the input no more than once, and this fact is immediately
    # obvious from the code.
    # Exits as soon as a bad combination has been detected.
    pos = neg = zero = False
    for item in iterable:
        if item > 0:
            pos = True
        elif item < 0:
            neg = True
        else:
            zero = True
        # Adjust the following statement if a different
        # treatment of zero is required.
        # Redundant parentheses added for clarity.
        if (pos and neg) or zero:
            return False
    return True

Upvotes: 1

aid
aid

Reputation: 161

Using the principal that multiplying your numbers gives a positive result if they all the same, else negative,

import operator

def all_same_sign(intlist):
    return reduce(operator.mul, intlist) > 0

>>> all_same_sign([-1, -4, -6, -8])
True
>>> all_same_sign([-1, -4, 12, -8])
False

This doesn't handle zeros though...

Upvotes: 0

John La Rooy
John La Rooy

Reputation: 304355

Here is one that works fine with generators etc. too

def all_same_sign(ints):
    ints = iter(ints)
    first_is_positive = next(ints) > 0
    return all( (x>0) == first_is_positive for x in ints)

If ints is empty, you get a StopIteration exception.

This version gorups 0 with the negative numbers. Use >= if you wish to group with the positive numbers instead

Upvotes: 1

Mark Byers
Mark Byers

Reputation: 838696

This might help you:

def all_same_sign(ints):
    return all(x < 0 for x in ints) or all(x > 0 for x in ints)

You may want to change < and > to <= and >= depending on how you want to treat 0.

Upvotes: 14

tcarobruce
tcarobruce

Reputation: 3838

After @EOL's solution, but works without list indexing or iterating multiple times.

def all_same_sign(sequence):
    items = iter(sequence)
    try:
        first = items.next() > 0
    except StopIteration:
        return True
    return all((item > 0) == first for item in items)

This also occurred to me, but doesn't take advantage of all/any short-circuiting:

def all_same_sign(sequence):
    return len(set(item > 0 for item in sequence)) <= 1

Upvotes: 3

Matthew Rankin
Matthew Rankin

Reputation: 461167

Why not take advantage of the fact that if all numbers are the same sign, then the sum of the absolute value of each individual number will be equal to the absolute value of the sum of each number?

def check_sign(queryset):
    return abs(sum(queryset)) == sum(map(abs, queryset))

Example Showing Details of the Math

Case 1: All numbers have the same sign

a = (-1, -4, -8)
sum(a) = -13
abs(sum(a)) = 13        # the absolute value of the tuple's sum
map(abs, a) = [1, 4, 8]
sum(map(abs, a)) = 13   # the tuple's sum of each element's absolute value

Both methods yield 13, so the signs are the same.

Case 2: Not all numbers have the same sign

b = (-1, 4, 8)
sum(b) = 11
abs(sum(b)) = 11        # the absolute value of the tuple's sum 
map(abs, b) = [1, 4, 8]
sum(map(abs, b)) = 13   # the tuple's sum of each element's absolute value

The methods yield different numbers (11 and 13), so the signs are not all the same.

Upvotes: 1

chriscauley
chriscauley

Reputation: 21041

If your numbers are sorted you only need to compare the ends. If not you could sort them:

def same_sign(numbers):
    numbers = sorted(numbers)
    #if numbers[0]==0: return True                Uncomment if you consider 0 positive
    if numbers[0]*numbers[-1]>0: return True
    return False

If you changed this to >=0 zero would be considered sign neutral. I'm not sure if this is a better implementation than the current answers, but it could be faster for large sets of data.

Upvotes: 0

Eric O. Lebigot
Eric O. Lebigot

Reputation: 94565

Here is a nice pythonic way of doing this (using all()):

from math import copysign

sign = lambda x: copysign(1, x)  # Sign function

def check_consistent_categories(sequence):
    main_sign = sign(sequence[0])
    return all(sign(y) == main_sign for y in sequence)

(for Python 2.6+, which introduced the math.copysign() function). This solution considers that 0 is positive.

Mark Byers' solution is more flexible, though, and it is also arguably more legible.

Upvotes: 1

ismail
ismail

Reputation: 47642

Just one unrelated nit, since you are doing this:

try:
    [...]
except:
    [...]

You are ignoring all of the exceptions, be very careful my friend, this will be hiding lots of bugs, instead always be precise while doing exception handling e.g:

try:
    [...]
except IndexError:
    # Handle out of index
except IOError:
    # Handle I/O error

etc. Keep this in mind while coding a larger python application.

Upvotes: 1

Related Questions