How to Use Data Types & the Python Type() Function

This article discusses types in the Python programming language and the uses of the Python type() function.

While not previously a standard for Python programming, this concept has allowed newer versions of Python to integrate much more smoothly with third-party tools that are made more for statically typed languages and give developers a powerful arsenal for creating their code.

If you’re ready to learn about both object types in Python and the Python type() function, let’s go!

Prerequisites : The reader is expected to have an understanding of Python programming and should have worked with basic Python types such as strings, numbers, etc. The code in this article has been run on Python 3.8. Type annotations were introduced in Python 3.5, so any code related to type annotations is not expected to run on previous versions of Python.

The Python type() function.

Python is a dynamically typed language i.e. a variable is not declared with a specific type as in languages like ‘C’, ‘Java’, etc.  A type is assigned to a variable when it is assigned a value. Gradual typing (type annotations/type hints) was introduced in PEP 484.  However, there is no compile/runtime checking of types. Annotations were introduced mainly for third-party static tools such as lint checkers to perform static checks on the code.

The built in function Python type() has 2 signatures.

1-Argument Version

This version simply returns a return value type object of the object passed in as a single parameter. We will use this signature in various examples below.

# Syntax : class type(object)

# examples
>>> type('Python Types')
<class 'str'>

>>> type(10)
<class 'int'>

3-Argument version

This version is used to dynamically create a new type object (class). We will look at this version later. The examples in the sections below use the 1-argument version of type() on various built-in and custom data types.

Robots painting a wall

Dynamic Typing

Implicit Typing

Python is a dynamically typed language i.e. the type of a variable comes from its value via assignment and not the variable itself. They are type checked just before an assignment or operation (e.g.. +) is performed on them.

# assignment
>>> x = 10
>>> type(x)
class 'int'>

# operation
>>> 'abc' + 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "int") to str
  1. x in the above example gets the type int only after 10 is assigned to it.
  2. In the second example, the type checking on ‘abc’ and 1 happens when the ‘+’ operator is applied.

Explicit Typing/Conversion

Python also allows explicit typing and conversion via various functions. The examples below explicitly convert from a string to a specific type.

>>> s = str("Python types")
>>> type(s)
<class 'str'>

>>> i = int('10')
>>> type(i)
<class 'int'>

>>> f = float('3.5')
>>> type(f)
<class 'float'>

Python Built-in Types

Python primitive types

The 4 primitives in Python are string, integer, float, boolean.

  • Strings are immutable sequences of Unicode characters.
  • Integers represent whole nos (e.g. 5, 10) from negative to positive infinity
  • Floats represent rational nos. Examples include decimal numbers such as 4.5.
  • Booleans represent True / False values.
# String
mystr = "Python Types"

>>> type(mystr)
<class 'str'>

# Int
>>> a = 9

>>> type(a)
<class 'int'>

# Float
>>> f = 4.55555566666

>>> type(f)
<class 'float'>

# Boolean
>>> b = False

>>> type(b)
<class 'bool'>

Photo of Python code on computer screen

Python Non-primitive types

Examples of Python non-primitive types include array, list, tuples, dictionary, and set.

  • Arrays are very similar in functionality to arrays in languages like ‘C’ and ‘Java. They are defined in the array module.
  • Lists store a sequence of values.  They are similar to arrays but allow elements of different data types to be mixed in a sequence. Duplicate elements are allowed.
  • Tuples are similar to lists but are immutable. Duplicate elements are allowed.
  • Dictionaries allow key-value pairs to be stored. The key is required to be immutable.
  • Sets store sequences of values with no duplicates.
>>> from array import array

# integer array
>>> arrint = array("i", [10, 5, 6,])

>>> type(arrint)
<class 'array.array'>
>>> arrint[2]= 15
>>> arrint
array('i', [10, 5, 15])

# list 
>>> l = [1, 2, 'abc'] 
>>> type(l) 
<class 'list'>

#tuple
t = (1,2,'abc')
>>> type(t) 
<class 'tuple'>

# dictionary
>>> d = {'sport': ''Basketball', 'greatest': 'Jordan'}
>>> type(d)
<class dict>

# set
>>> s = set(['John', 'Henry', 'Pooja', 'Katherine'])
>>> type(s)
<class 'set'>

Custom types

Any user-defined class is a custom type in Python. A classes members can be a combination of Python primitive data types and other custom types. Below in the following code, we define a new type (class) called Person.

class Person:
    Person(self, fname, lname, dob, city, state, country) :
        self.first_name = fname
        self.last_name  = lname
        self.dob        = dob
        self.adress     = address
    
>>> p = Person('John', 'Fowler', '05/10/1999', 'Raleigh', 'NC', 'USA')
>>> print (type(p))

<class '__main__.Person'>

3-argument version of type()

The 3-argument version of the type() function returns a new type object.

 class type(name, bases, dict, **kwds)

The name (__name__ attribute) argument is the class name. The bases (__bases__ attribute) argument is the list of base classes  and defaults to object if left unspecified. dict (__dict__ attribute) contains the attributes and methods of the class.

The 2 following statements are equivalent. This version is thus a dynamic way to declare a class.

# static definition
class Person :
    name = "Guido"

# dynamic definition
Person2 = type('Person2', (), dict(name='Guido'))

# class dictionary
>>> Person2.__dict__
mappingproxy({'name': 'Guido', '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person2' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None})

# instance
>>> p = Person2()
>>> p.role = "BDFL"

# instance dictionary
>>> p.__dict__
{'role': 'BDFL'}
  1. Define a static class Person2 with a single attribute name assigned to value Guido.
  2. Define a dynamic class with the name Person2 and a class level attribute name assigned to value Guido.
  3. Print the class level dictionary. We see the name attribute.
  4. Create an instance p of type Person2.
  5. Attach an instance level attribute role (with value “BDFL”) to it.
  6. Print the instance level dictionary. We see the role attribute.

Let’s look at a more complete example of dynamic class creation and use. The example below contrasts the static definition of the Person class with the dynamic definition of the Person2 class.  The Person2 class dynamically defines its class attributes (counter), its constructor __init__ and a method name2, whose definition is the function fn_name2.

# static class definition
class Person:
    count = 0

    def __init__(self, name):
        self.name = name

    def name(self):
        return "Hi, I am " + self.name

# dynamic class definition

# define methods to be attached to the class definition.
def init_Person(self, name):
    self.name = name

def fn_name2(self):
return "Hello, I am " + self.name

# dynamic class definition
Person2 = type("Person2", (), { "counter":0,
                                "__init__": init_Person,
                                "name2":fn_name2
                              })

>>> x = Person2("Python Types")
>>> print(x.name)
Python Types

>>> print(x.name2())
Hello, I am Python Types

>>> print(x.__dict__)
{'name': 'Python Types'}
  1. Define the static class Person for reference.
  2. Define methods init_Person and fn_name2. These methods will be used in the dynamic class definition Person2.
  3. Define the dynamic class Person2. Similar to Person, It has a class level attribute, counter, an __init__ constructor initialized to init_Person and the name2 method initialized to fn_name2.
  4. Create an object of class Person2 passing in “Python Types” as the name.
  5. Print the object’s name attribute. This shows that the __init__ method of Person2 i.e. init_Person was invoked and that the name attribute was initialized to “Python Types”.
  6. Invoke the object’s name2 method. This prints out the “Hello, I am Python Types” message indicating that the fn_name2 method was invoked properly.
  7. Finally print the object’s dictionary which shows the name attribute.

Two screens being looked at through glasses

Python Type Annotations

Type Annotations is the closest that Python gets to compile-time type checking in languages like ‘C’. Added in Python 3.5 (via PEP 484), this feature is a mere hint to external tools as Visual IDEs or tools such mypy to produce errors when run on the code. These type annotations are not enforceable by the Python runtime. Thus annotations are a useful feature to use in the CI/CD pipeline (via tools like mypy) to detect errors that may cause crashes later.

python interpreter not enforcing annotation

# add10.py - annotation
def add10(n: int) -> int: 
    return n + 10

(mypy) >python add10.py 
Traceback (most recent call last):
File "add10.py", line 6, in <module>
add10("10")
File "add10.py", line 3, in add10
return n + 10 
TypeError: can only concatenate str (not "int") to str

mypy enforcing annotation.

(mypy)> mypy add10.py 
add10.py:6: error: Argument 1 to "add10" has incompatible type "str"; expected "int"
Found 1 error in 1 file (checked 1 source file)

type Vs isinstance

type() only displays the object’s class whereas isinstance takes inheritance into account. The Python documentation recommends the use of isinstance over type for this reason.

class A:
    i = 10

    def __init__(self, name) :
        self.name = name

class B(A) :
    def __init__(self, name, age) :
        super().__init__(name)
        self.age = age

    def __str__(self):
        return (self.name + ':' + str(self.age))


>>> b = B('Mukul', 10)
>>> print (str(b))
Mukul:10
>>> print (type(b))
<class '__main__.B'>

# b is an instance of B and A
>>> print (isinstance(b, B))
True
>>> print (isinstance(b, A))
True

Person at a computer using a mouse

References / Additional Reading:

Besides this article, you can check out more about Python type() and Python in general via the following links below.

Conclusion

And that brings this explanation to a close. By now, you should have a stronger understanding not only in how data types are handled within Python programming, but how Python type() is a useful function to quickly make sure that a Python object is of a particular variable type. The single argument version of type() allows us to do this. As discussed, though, while type() reveals the object type, it does not take the inheritance chain of the object into consideration. We can use the isinstance() function to check the parent classes of the object

While type annotations are not enforced by the Python runtime, they can still be extremely useful (just as many built in functions for Python are). For instance, they help external IDEs and tools such as mypy to provide error messages when a type violation is found in the code. For any experienced coders, you’ll know the more information when debugging the better. However, the uses here are fairly endless, and all just depend on your particular program’s needs. Either way, we hope you get out there and do some Python programming of your very own!

Did you come across any errors in this tutorial? Please let us know by completing this form and we’ll look into it!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.