How Does Tuple Comparison Work in Python

How does tuple comparison work in Python?

Tuples are compared position by position:
the first item of the first tuple is compared to the first item of the second tuple; if they are not equal (i.e. the first is greater or smaller than the second) then that's the result of the comparison, else the second item is considered, then the third and so on.

See Common Sequence Operations:

Sequences of the same type also support comparisons. In particular, tuples and lists are compared lexicographically by comparing corresponding elements. This means that to compare equal, every element must compare equal and the two sequences must be of the same type and have the same length.

Also Value Comparisons for further details:

Lexicographical comparison between built-in collections works as follows:

  • For two collections to compare equal, they must be of the same type, have the same length, and each pair of corresponding elements must compare equal (for example, [1,2] == (1,2) is false because the type is not the same).
  • Collections that support order comparison are ordered the same as their first unequal elements (for example, [1,2,x] <= [1,2,y] has the same value as x <= y). If a corresponding element does not exist, the shorter collection is ordered first (for example, [1,2] < [1,2,3] is true).

If not equal, the sequences are ordered the same as their first differing elements. For example, cmp([1,2,x], [1,2,y]) returns the same as cmp(x,y). If the corresponding element does not exist, the shorter sequence is considered smaller (for example, [1,2] < [1,2,3] returns True).

Note 1: < and > do not mean "smaller than" and "greater than" but "is before" and "is after": so (0, 1) "is before" (1, 0).

Note 2: tuples must not be considered as vectors in a n-dimensional space, compared according to their length.

Note 3: referring to question https://stackoverflow.com/questions/36911617/python-2-tuple-comparison: do not think that a tuple is "greater" than another only if any element of the first is greater than the corresponding one in the second.

Tuple comparison in Python

Your understanding is flawed. It's not and - it's a cascading comparison.

a < d or (a == d and b < e) or (a == d and b == e and c < f)

Another way of understanding this for arbitrary length tuples...

def tuple_less_than(tuple1, tuple2):
for item1, item2 in zip(tuple1, tuple2):
if item1 != item2:
return item1 < item2
return len(tuple1) < len(tuple2)

How does tuple comparison work under the hood

The standard library defines tuple comparison operators for 2 element tuples up to and including 6 element tuples. It does this through using the metaprogramming tool GYB (Generate Your Boilerplate) which allows embedded Python code to generate a file from a template.

The resulting tuple comparison operators end up looking like:

public func == <A, B>(lhs: (A, B), rhs: (A, B)) -> Bool

public func == <A, B, C>(lhs: (A, B, C), rhs: (A, B, C)) -> Bool

public func == <A, B, C, D>(lhs: (A, B, C, D), rhs: (A, B, C, D)) -> Bool

// ...

So with your code:

let p1 = (name: "John", age: 12)
let p2 = (color: "Red", size: 12)

if p1 == p2 {
print("equal")
} else {
print("not equal")
}

The compiler will call the two element tuple comparison operator. You'll note that none of the defined tuple comparison operators use tuple labels for their parameters. The reason why both
(name: String, age: Int) and (color: String, size: Int) can be passed to (A, B) is that the compiler implements an implicit conversion that can strip a tuple of its labels. So both arguments have their labels stripped and are both passed as (String, Int).

That's what makes the following legal:

let p1 = (name: "John", age: 12)
let p2: (String, Int) = p1 // Legal.

Things get weirder with the fact that the compiler also has an implicit conversion that can add arbitrary labels to a tuple:

let p1 = ("John", 12)
let p2: (foo: String, bar: Int) = p1 // Legal.

However the direct renaming of labels is forbidden:

// Indirect renaming ✅
let p1 = (name: "John", age: 12)
let p2: (String, Int) = p1
let p3: (foo: String, bar: Int) = p2

// Put together ✅
let p4: (foo: String, bar: Int) = p1 as (String, Int)

// Direct renaming ❌
let p4 = (name: "John", age: 12)
let p5: (foo: String, bar: Int) = p4

And that's the behaviour you're seeing with your as? cast. Like the above example, first coercing to the unlabelled form works:

func givePerson() -> (name: String, age: Int)? {
return ("alex", 2)
}

func extract() {
var p3: (Name: String, age: Int)
if let person = givePerson() as (String, Int)? as? (Name: String, age: Int) {
p3 = person
print(p3)
} else {
print("p3 not defined")
}

}

extract() // (Name: "alex", age: 2)

python tuple comparison, 'less than' revisited (2.7)

The answer lies in word 'lexigographicly'. It means, that python compares tuples beginning from the first position. This order is used in vocabularies or lexicons - word a is smaller than word b, if a apperars in vocabulary before b. Then we can compare two words like 'anthrax' and 'antipodes', where three first letters are equal: 'anthrax' appears in vocabulary before 'antipodes', so the statement 'anthrax' < 'antipodes' is True.

This comparision can be represented like this:

def isSmaller(a, b): # returns true if a<b, false if a>=b
for i in xrange(0, a.__len__()): # for every elementof a, starting from left
if b.__len__() <= i: # if a starts with b, but a is longer, eg. b='ab', a='ab...'
return False
if a[i] < b[i]: # if a starts like b, but there is difference on i-th position,
# eg. a='abb...', b='abc...',
return True
if a[i] > b[i]: # eg. a='abc...', b='abb...',
return False
if a[i] == b[i]: # if there is no difference, check the next position
pass
if a.__len__() < b.__len__(): # if b starts with a, but b is longer, eg. a='ac', b='ac...'
return True
else: # else, ie. when a is the same as b, eg. a='acdc', b='acdc'
return False


print (1,2,3)<(1,2)
print (1,2,3)<(1,2,3)
print (1,2,3)<(1,3,2)
print isSmaller((1,2,3),(1,2))
print isSmaller((1,2,3),(1,2,3))
print isSmaller((1,2,3),(1,3,2))

Output:

False
False
True
False
False
True

Tuple comparison in function

It's because you are using *values rather than values in your function definition

When you use the special syntax *args in a function, args will already come back as a tuple, where each arg is an element of the tuple.

So for example

> def print_args(*args):
print(args)
> print_args('a', 'b', 'c')

# Outputs:
('a', 'b', 'c')

In your case since you are passing in a tuple already, w/in the function values is like "Ok, I'll happily take a tuple as my first argument", and values becomes a tuple of tuples (well a tuple of a single tuple). Thus you are comparing ('a',) to 'a' and your check fails

TL;DR: either pass in just 'a' or change *values to values

def test(values):
return values[0] == 'a'

tuple = ('a',)
test(tuple)

# Outputs:
True

Comparing all elements of two tuples (with all() functionality)

You can achieve this with a list comprehension and the zip built-in:

>>> a = (100, 0)
>>> b = (50, 50)
>>> [(a > b) for a, b in zip(a,b)]
[True, False]

You can use all() or any() on the returned list.

Grammar of tuple comparisons

First of all, I am a little confused about "failing" and "succeeding", since no entries of your dictionary actually satisfy your request, so it makes sense that the succeeding case is the latter one, where no results are returned.

And, actually, that makes sense, because tuples are ordered lexicographically, meaning that (a, b) ≥ (c, d) iff a > c or (a == c and b ≥ d). This generalizes to not only couples, but any tuple. So when you are checking for x[1] > (6, 70), it's not at all equivalent to x[1][0] > 6 and x[1][1] > 70, hence the different results.

Tuple Equivalence in python

Such things are defined in the language reference, not in the tutorial.

Tuples and lists are compared lexicographically using comparison of
corresponding elements. This means that to compare equal, each element
must compare equal and the two sequences must be of the same type and
have the same length.

Tuple Comparison

what am I missing? d['close'] > d['MA']?

Edit: Re, your comments

[...] what I want to return is how many times one element of "close" is > to the matching element of MA . (same tuple index)

sum( pair[0] > pair[1] for pair in zip(d['close'], d['MA']) )


Related Topics



Leave a reply



Submit