开发者

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'v开发者_运维百科e 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


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.


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


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.


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.


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.


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


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


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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜