In Python, suppose one wants to test whether the variable x
is a reference to a list object. Is there a difference between if type(x) is list:
and if type(x) == list:
? This is how I understand it. (Please correct me if I am wrong)
type(x) is list
tests whether the expressions type(x)
and list
evaluate to the same object and type(x) == list
tests whether the two objects have equivalent (in some sense) values.type(x) == list
should evaluate to True
as long as x
is a list. But can type(x)
evaluate to a different object from what list
refers to?What exactly does the expression list
evaluate to? (I am new to Python, coming from C++, and still can't quite wrap my head around the notion that types are also objects.) Does list
point to somewhere in memory? What data live there?
The "one obvious way" to do it, that will preserve the spirit of "duck typing" is isinstance(x, list)
. Rather, in most cases, one's code won't be specific to a list, but could work with any sequence (or maybe it needs a mutable sequence). So the recomendation is actually:
from collections.abc import MutableSequence
...
if isinstance(x, MutableSequence):
...
Now, going into your specific questions:
What exactly does the expression list evaluate to? Does list point to somewhere in memory? What data live there?
list
in Python points to a class. A class that can be inherited, extended, etc...and thanks to a design choice of Python, the syntax for creating an instance of a class is indistinguishable from calling a function.
So, when teaching Python to novices, one could just casually mention that list
is a "function" (I prefer not, since it is straightout false - the generic term for both functions and classes in regards to that they can be "called" and will return a result is callable
)
Being a class, list
does live in a specific place in memory - the "where" does not make any difference when coding in Python - but yes, there is one single place in memory where a class, which in Python is also an object, an instance of type
, exists as a data structure with pointers to the various methods that one can use in a Python list.
As for:
type(x) is list
tests whether the expressions type(x) and list evaluate to the same object and type(x) == list tests whether the two objects have equivalent (in some sense) values.
That is correct: is
is a special operator that unlike others cannot be overriden for any class and checks for object itentity - in the cPython implementation, it checks if both operands are at the same memory address (but keep in mind that that address, though visible through the built-in function id
, behaves as if it is opaque from Python code). As for the "sense" in which objects are "equal" in Python: one can always override the behavior of the ==
operator for a given object, by creating the special named method __eq__
in its class. (The same is true for each other operator - the language data model lists all available "magic" methods).
For lists, the implemented default comparison automatically compares each element recursively (calling the .__eq__
method for each item pair in both lists, if they have the same size to start with, of course)
type(x) == list should evaluate to True as long as x is a list. But can type(x) evaluate to a different object from what list refers to?
Not if "x" is a list proper: type(x) will always evaluate to list
. But ==
would fail if x were an instance of a subclass of list
, or another Sequence implementation: that is why it is always better to compare classes using the builtins isinstance
and issubclass
.