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 asx <= 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
Converting Unix Timestamp String to Readable Date
How to Convert All Strings in a List of Lists to Integers
How to Get the Source Code of a Python Function
"Pip Install Unroll": "Python Setup.Py Egg_Info" Failed With Error Code 1
How to Print a Single Backslash
Pandas Groupby With Delimiter Join
How to Add Hovering Annotations to a Plot
What Is the Result of % in Python
How to Add New Keys to a Dictionary
Python Multiprocessing Picklingerror: Can't Pickle ≪Type 'Function'≫
How to Fix "Runtimeerror: Package Fails to Pass a Sanity Check" For Numpy and Pandas
Finding Local Ip Addresses Using Python'S Stdlib
Find the Current Directory and File'S Directory
String Comparison in Python: Is Vs. ==
Pygame Window Not Responding After a Few Seconds
"Unicode Error "Unicodeescape" Codec Can't Decode Bytes... Cannot Open Text Files in Python 3