Why does the expression below evaluate to False? Both the values in the comparison are Falsy.
print('' == [])
According to the documentation:
An object’s type determines the operations that the object supports (e.g., “does it have a length?”) and also defines the possible values for objects of that type.
Furthermore, it also states:
== compares the values of two objects
However, since the value of an object in Python is pretty much abstract, in that there is no canonical method to access it, the default behavior of == compares the identity of the two objects (which can be thought of as the object’s address in memory) but is defined as:
An integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same identity.
It's also important to mention that many built-in types have customized comparison methods within their respective types that compare based on their 'values'.
While it's true that both of your objects are Falsy, they are of different types (String and List), and as a result, the default behavior of comparing identities is used. Since there is no chance of the two objects having non-overlapping lifetimes, their identities will be different, and so '' == [] will evaluate to False.
In order to compare the Truthy values of two objects, first convert them to booleans as Waket suggested in the comments:
bool('') == bool([])
taking advantage of Python's truth value testing.
Related
I find it odd that in Python, {1} == frozenset({1}) evaluates to True. set and frozenset are different object types, and I don't see this similarity between other iterable object types (ex. {1} == (1,) evaluates to False). Why does this behavior occur with sets? Are there other iterable object types that have similar behavior?
As per the documentation python2 and documentation python3
Instances of set are compared to instances of frozenset based on
their members. For example, "set('abc') == frozenset('abc')" returns
True.
and in the python3 documentation:
Both set and frozenset support set to set comparisons. Two sets are equal if and only if every element of each set is contained in the other (each is a subset of the other). A set is less than another set if and only if the first set is a proper subset of the second set (is a subset, but is not equal). A set is greater than another set if and only if the first set is a proper superset of the second set (is a superset, but is not equal).
I just wrote a testing script for a project I have in python, and I learned that checking if the values of two objects are equivalent is not as simple as foo == bar. For example, I have an object dashboard that has a pandas dataframe as an attribute, so I defined its __eq__ method as:
def __eq__(self, other):
vals = [self.__dict__[k] == other.__dict__[k] for k in self.__dict__.keys()]
vals = [v if isinstance(v, bool) else all(v) for v in vals]
return all(vals)
It compares the dictionaries of each object, and if any of these comparisons yields something other than a boolean (e.g., a dataframe) it applies all() to reduce it to a single boolean. I then apply all() to this entire list of attribute comparisons to test whether or not every attribute of self and other are equivalent.
I used this __eq__ definition in several classes, and also used something similar for a comparison method in my parent Test class. I got my test to work, but I'm curious if there's a more elegant/efficient way to handle this. (Disclaimer: Testing is new to me, as well as OOP in general.)
No, there is not a general method for testing equality of objects.
This is partly because custom/arbitrary objects have widely varying interpretations of what equality means. For example, in your code you've defined equality to mean "the values are booleans and/or iterables of booleans and all values agree", but this rule ONLY applies to keys that both dictionaries have in common. Someone else might need to check that a dictionary has all the same keys but doesn't care about the values, or that all keys and all values are identical and of specific datatypes, etc. So, this is left to the user to implement.
to compare dataframes, since you mention this, there is:
DataFrame.equals(other)
https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.equals.html
Example:
>>> tuple((1, 2)) == tuple((2, 1))
False
>>> frozenset((1, 2)) == frozenset((2, 1))
True
Frozen sets are immutable. I would expect that equality between immutable objects should by determined by order, but here obviously that is not the case.
How can I discard frozensets with same elements and different order, without casting to different type?
The short answer is you can't, since as pointed out in the comments, sets and frozensets are unordered data structures. Here are some excerpts from the docs* to support this statement:
A set object is an unordered collection of distinct hashable objects.
There are currently two built-in set types, set and frozenset. The set type is mutable — the contents can be changed using methods like add() and remove(). Since it is mutable, it has no hash value and cannot be used as either a dictionary key or as an element of another set. The frozenset type is immutable and hashable — its contents cannot be altered after it is created; it can therefore be used as a dictionary key or as an element of another set.
* Python 2.7.12
For a better grasp of the equality issue, I would encourage you to run the following snippet using the Online Python Tutor:
tup_1 = tuple((1, 2))
tup_2 = tuple((2, 1))
fs_1 = frozenset((1, 2))
fs_2 = frozenset((2, 1))
This is an extremely handy tool that renders a graphical representation of the objects in memory while the code is executed step by step. I'm attaching a screenshot:
The answer by Tonechas is wrong.
frozenset is unordered, but it can be used for equality comparison. Quote from the python official doc:
Both set and frozenset support set to set comparisons. Two sets are equal if and only if every element of each set is contained in the other (each is a subset of the other). A set is less than another set if and only if the first set is a proper subset of the second set (is a subset, but is not equal). A set is greater than another set if and only if the first set is a proper superset of the second set (is a superset, but is not equal).
I have two user-defined objects, say a and b.
Both these objects have the same hash values.
However, the id(a) and id(b) are unequal.
Moreover,
>>> a is b
False
>>> a == b
True
From this observation, can I infer the following?
Unequal objects may have the same hash values.
Equal objects need to have the same id values.
Whenever obj1 is obj2 is called, the id values of both objects is compared, not their hash values.
There are three concepts to grasp when trying to understand id, hash and the == and is operators: identity, value and hash value. Not all objects have all three.
All objects have an identity, though even this can be a little slippery in some cases. The id function returns a number corresponding to an object's identity (in cpython, it returns the memory address of the object, but other interpreters may return something else). If two objects (that exist at the same time) have the same identity, they're actually two references to the same object. The is operator compares items by identity, a is b is equivalent to id(a) == id(b).
Identity can get a little confusing when you deal with objects that are cached somewhere in their implementation. For instance, the objects for small integers and strings in cpython are not remade each time they're used. Instead, existing objects are returned any time they're needed. You should not rely on this in your code though, because it's an implementation detail of cpython (other interpreters may do it differently or not at all).
All objects also have a value, though this is a bit more complicated. Some objects do not have a meaningful value other than their identity (so value an identity may be synonymous, in some cases). Value can be defined as what the == operator compares, so any time a == b, you can say that a and b have the same value. Container objects (like lists) have a value that is defined by their contents, while some other kinds of objects will have values based on their attributes. Objects of different types can sometimes have the same values, as with numbers: 0 == 0.0 == 0j == decimal.Decimal("0") == fractions.Fraction(0) == False (yep, bools are numbers in Python, for historic reasons).
If a class doesn't define an __eq__ method (to implement the == operator), it will inherit the default version from object and its instances will be compared solely by their identities. This is appropriate when otherwise identical instances may have important semantic differences. For instance, two different sockets connected to the same port of the same host need to be treated differently if one is fetching an HTML webpage and the other is getting an image linked from that page, so they don't have the same value.
In addition to a value, some objects have a hash value, which means they can be used as dictionary keys (and stored in sets). The function hash(a) returns the object a's hash value, a number based on the object's value. The hash of an object must remain the same for the lifetime of the object, so it only makes sense for an object to be hashable if its value is immutable (either because it's based on the object's identity, or because it's based on contents of the object that are themselves immutable).
Multiple different objects may have the same hash value, though well designed hash functions will avoid this as much as possible. Storing objects with the same hash in a dictionary is much less efficient than storing objects with distinct hashes (each hash collision requires more work). Objects are hashable by default (since their default value is their identity, which is immutable). If you write an __eq__ method in a custom class, Python will disable this default hash implementation, since your __eq__ function will define a new meaning of value for its instances. You'll need to write a __hash__ method as well, if you want your class to still be hashable. If you inherit from a hashable class but don't want to be hashable yourself, you can set __hash__ = None in the class body.
Unequal objects may have the same hash values.
Yes this is true. A simple example is hash(-1) == hash(-2) in CPython.
Equal objects need to have the same id values.
No this is false in general. A simple counterexample noted by #chepner is that 5 == 5.0 but id(5) != id(5.0).
Whenever, obj1 is obj2 is called, the id values of both the objects is compared, not their hash values.
Yes this is true. is compares the id of the objects for equality (in CPython it is the memory address of the object). Generally, this has nothing to do with the object's hash value (the object need not even be hashable).
The hash function is used to:
quickly compare dictionary keys during a dictionary lookup
the ID function is used to:
Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.
I just had an experiment , which assigned same whole number to 2 separated variables , but when I used ' is ' operator to compare them , then in returned True ; I didn't expect such a thing , Thought that the variables must be exact same like==>
a = 10
b = a
a is b # output == True
What does this mean?
The only types of values not acceptable as dictionary keys are values containing lists or dictionaries or other mutable types that are compared by value rather than by object identity, the reason being that the efficient implementation of dictionaries requires a key’s hash value to remain constant.
I think even for tuples, comparison will happen by value.
The problem with a mutable object as a key is that when we use a dictionary, we rarely want to check identity. For example, when we use a dictionary like this:
a = "bob"
test = {a: 30}
print(test["bob"])
We expect it to work - the second string "bob" may not be the same as a, but it is the same value, which is what we care about. This works as any two strings that equate will have the same hash, meaning that the dict (implemented as a hashmap) can find those strings very efficiently.
The issue comes into play when we have a list as a key, imagine this case:
a = ["bob"]
test = {a: 30}
print(test[["bob"]])
We can't do this any more - the comparison won't work as the hash of a list is not based on it's value, but rather the instance of the list (aka (id(a) != id(["bob"))).
Python has the choice of making the list's hash change (undermining the efficiency of a hashmap) or simply comparing on identity (which is useless in most cases). Python disallows these specific mutable keys to avoid subtle but common bugs where people expect the values to be equated on value, rather than identity.
The documentation mixes together two different things: mutability, and value-comparable. Let's separate them out.
Immutable objects that compare by identity are fine. The identity can
never change, for any object.
Immutable objects that compare by value are fine. The value can never
change for an immutable object. This includes tuples.
Mutable objects that compare by identity are fine. The identity can
never change, for any object.
Mutable objects that compare by value are not acceptable. The value
can change for a mutable object, which would make the dictionary
invalid.
Meanwhile, your wording isn't quite the same as Mapping Types (4.10 in Python 3.3 or 5.8 in Python 2.7, both of which say:
A dictionary’s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys.
Anyway, the key point here is that the rule is "not hashable"; "mutable types (that are compared by value rather than by object identity)" is just to explain things a little further. It isn't strictly true that comparing by object identity and hashing by object identity are always the same (the only thing that's required is that if id is equal, the hash is equal).
The part about "efficient implementation of dictionaries" from the version you posted just adds to the confusion (which is probably why it's not in the reference documentation). Even if someone came up with an efficient way to deal with storing lists as dict keys tomorrow, the language doesn't allow it.
A hash is way of calculating an unique code for an object, this code always the same for the same object. hash('test') for example is 2314058222102390712, so is a = 'test'; hash(a) = 2314058222102390712.
Internally a dictionary value is searched by the hash, not by the variable you specify. A list is mutable, a hash for a list, if it where defined, would be changing whenever the list changes. Therefore python's design does not hash lists. Lists therefore can not be used as dictionary keys.
Tuples are immutable, therefore tubles have hashes e.G. hash((1,2)) = 3713081631934410656. one could compare whether a tuple a is equal to the tuple (1,2) by comparing the hash, rather than the value. This would be more efficient as we have to compare only one value instead of two.