462

Unless I'm mistaken, creating a function in Python works like this:

def my_func(param1, param2):
    # stuff

However, you don't actually give the types of those parameters. Also, if I remember, Python is a strongly typed language, as such, it seems like Python shouldn't let you pass in a parameter of a different type than the function creator expected. However, how does Python know that the user of the function is passing in the proper types? Will the program just die if it's the wrong type, assuming the function actually uses the parameter? Do you have to specify the type?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Leif Andersen
  • 21,580
  • 20
  • 67
  • 100

14 Answers14

1092

The other answers have done a good job at explaining duck typing and the simple answer by tzot:

Python does not have variables, like other languages where variables have a type and a value; it has names pointing to objects, which know their type.

However, one interesting thing has changed since 2010 (when the question was first asked), namely the implementation of PEP 3107 (implemented in Python 3). You can now actually specify the type of a parameter and the type of the return type of a function like this:

def pick(l: list, index: int) -> int:
    return l[index]

Here we can see that pick takes 2 parameters, a list l and an integer index. It should also return an integer.

So here it is implied that l is a list of integers which we can see without much effort, but for more complex functions it can be a bit confusing as to what the list should contain. We also want the default value of index to be 0. To solve this you may choose to write pick like this instead:

def pick(l: "list of ints", index: int = 0) -> int:
    return l[index]

Note that we now put in a string as the type of l, which is syntactically allowed, but it is not good for parsing programmatically (which we'll come back to later).

It is important to note that Python won't raise a TypeError if you pass a float into index, the reason for this is one of the main points in Python's design philosophy: "We're all consenting adults here", which means you are expected to be aware of what you can pass to a function and what you can't. If you really want to write code that throws TypeErrors you can use the isinstance function to check that the passed argument is of the proper type or a subclass of it like this:

def pick(l: list, index: int = 0) -> int:
    if not isinstance(l, list):
        raise TypeError
    return l[index]

More on why you should rarely do this and what you should do instead is talked about in the next section and in the comments.

PEP 3107 does not only improve code readability but also has several fitting use cases which you can read about here.


Type annotation got a lot more attention in Python 3.5 with the introduction of PEP 484 which introduces a standard module typing for type hints.

These type hints came from the type checker mypy (GitHub), which is now PEP 484 compliant.

The typing module comes with a pretty comprehensive collection of type hints, including:

  • List, Tuple, Set, Dict - for list, tuple, set and dict respectively.
  • Iterable - useful for generators.
  • Any - when it could be anything.
  • Union - when it could be anything within a specified set of types, as opposed to Any.
  • Optional - when it might be None. Shorthand for Union[T, None].
  • TypeVar - used with generics.
  • Callable - used primarily for functions, but could be used for other callables.

These are the most common type hints. A complete listing can be found in the documentation for the typing module.

Here is the old example using the annotation methods introduced in the typing module:

from typing import List

def pick(l: List[int], index: int) -> int:
    return l[index]

One powerful feature is the Callable which allows you to type annotate methods that take a function as an argument. For example:

from typing import Callable, Any, Iterable

def imap(f: Callable[[Any], Any], l: Iterable[Any]) -> List[Any]:
    """An immediate version of map, don't pass it any infinite iterables!"""
    return list(map(f, l))

The above example could become more precise with the usage of TypeVar instead of Any, but this has been left as an exercise to the reader since I believe I've already filled my answer with too much information about the wonderful new features enabled by type hinting.


Previously when one documented Python code with for example Sphinx some of the above functionality could be obtained by writing docstrings formatted like this:

def pick(l, index):
    """
    :param l: list of integers
    :type l: list
    :param index: index at which to pick an integer from *l*
    :type index: int
    :returns: integer at *index* in *l*
    :rtype: int
    """
    return l[index]

As you can see, this takes a number of extra lines (the exact number depends on how explicit you want to be and how you format your docstring). But it should now be clear to you how PEP 3107 provides an alternative that is in many (all?) ways superior. This is especially true in combination with PEP 484 which, as we have seen, provides a standard module that defines a syntax for these type hints/annotations that can be used in such a way that it is unambiguous and precise yet flexible, making for a powerful combination.

In my personal opinion, this is one of the greatest features in Python ever. I can't wait for people to start harnessing the power of it. Sorry for the long answer, but this is what happens when I get excited.


An example of Python code which heavily uses type hinting can be found here.

erb
  • 14,503
  • 5
  • 30
  • 38
  • I get a syntax error on 'def pick(l: list, index: int) -> int:' – rickfoosusa Apr 22 '15 at 15:22
  • 2
    @rickfoosusa: I suspect you are not running Python 3 in which the feature was added. – erb Apr 22 '15 at 20:21
  • 47
    Wait a minute! If defining parameter and return type does not raise a `TypeError`, what is the point of using `pick(l: list, index: int) -> int` like one-line defining then? Or I got it wrong, I don't know. – Eray Erdin Feb 21 '16 at 21:49
  • 40
    @Eray Erdin: That's a common misunderstanding and not at all a bad question. It can be used for documentation purposes, helps IDEs do better autocompletion and find errors ahead of runtime by using static analysis (just like mypy that I mentioned in the answer). There are hopes that the runtime could take advantage of the information and actually speed up programs but that's likely going to take very long to get implemented. You *might* also be able to create a decorator that throws the TypeErrors for you (the information is stored in the `__annotations__` attribute of the function object). – erb Feb 22 '16 at 07:29
  • 2
    @ErdinEray I should add that throwing TypeErrors is a bad idea (debugging is never fun, no matter how well intended exceptions are raised). But do not fear, the advantage of the new features described in my answer enables a better way: do not rely on any checking at runtime, do everything ahead of runtime with mypy or use an editor which does the static analysis for you such as PyCharm. – erb Apr 14 '16 at 12:36
  • 2
    what if the function returns two or more objects? I have tried `def pick(l: list, index: int = 0) -> int int:` and `def pick(l: list, index: int = 0) -> int, int:` and none of these work – Tony Feb 09 '17 at 19:04
  • 9
    @Tony: When you return two or more objects you actually return a tuple, so you should use the Tuple type annotation, i.e. `def f(a) -> Tuple[int, int]:` – erb Feb 11 '17 at 13:55
  • 1
    Great answer! It would by nice to test, if this feature can or can not improve performance like in Julia language. – Bobesh Mar 23 '18 at 14:45
  • @erb slight correction : typing module for map collection type is called Mapping not Map. See eg: from typing import Mapping – Innovator-programmer Oct 22 '21 at 09:44
  • 1
    Yes, _"We're all consenting adults here"_, BUT I easily made a mistake while refactoring my program, where I fed up the function with a mix of different objects and integers, despite having `int` type hint on the function argument, and it cost me a few hours of debugging. The impossible situation in many programming languages. – Alexander Fadeev Oct 27 '21 at 11:19
  • @AlexanderFadeev I find that if I have mypy configured well, refactoring is almost as easy as in statically typed languages. Hard to beat for sure, but a lot better than nothing! – erb Oct 27 '21 at 17:35
  • Instead of using if statements with isinstance, I would recommend using assertions, if you want shorter code. E.g. `assert isinstance(l, list)`. – Brōtsyorfuzthrāx Sep 02 '22 at 12:14
  • I would recommend using assertions rather than type descriptions in the comments. That way you can not only see the expected type in the code, but also check during runtime if the variable really is of expected type. Eg: `assert isinstance(l, list), f"'l' is expected to be a list - meanwhile {type(l)} was passed"` `assert isinstance(index, int), f"'index' is expected to be an integer - meanwhile {type(index)} was passed"` – Jasio Feb 11 '23 at 17:16
  • In `def pick(l: "list of ints", index: int = 0) -> int:` what is the meaning of `"list of ints"`. Is that any way of defining types? Why did you say the type of `l` is `str`? – Dhruv Jun 30 '23 at 18:25
  • Got it, "list of ints" is a string used to define the type of `l`. Using string to define type is allowed as shown in [PEP 3107](http://www.python.org/dev/peps/pep-3107/). I thought python itself did type checking and this string was not a method to write data type but both ideas were wrong. – Dhruv Jul 09 '23 at 07:22
223

Python is strongly typed because every object has a type, every object knows its type, it's impossible to accidentally or deliberately use an object of a type "as if" it was an object of a different type, and all elementary operations on the object are delegated to its type.

This has nothing to do with names. A name in Python doesn't "have a type": if and when a name's defined, the name refers to an object, and the object does have a type (but that doesn't in fact force a type on the name: a name is a name).

A name in Python can perfectly well refer to different objects at different times (as in most programming languages, though not all) -- and there is no constraint on the name such that, if it has once referred to an object of type X, it's then forevermore constrained to refer only to other objects of type X. Constraints on names are not part of the concept of "strong typing", though some enthusiasts of static typing (where names do get constrained, and in a static, AKA compile-time, fashion, too) do misuse the term this way.

Lukas
  • 2,232
  • 3
  • 21
  • 34
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • 94
    So it seems strong typing is not so strong, in this particular case, it's weaker than static typing.IMHO, compile time typing constraint on name/variable/reference is actually quite important, thus I boldly claim python is not as good as static typing on this aspect. Please correct me if I'm wrong. – liang Oct 13 '13 at 14:25
  • 30
    @liang That's an opinion, so you cannot be right or wrong. It is certainly also my opinion, and I've tried many languages. The fact that I cannot use my IDE to find out the type (and thus members) of the parameters is a major drawback of python. If this drawback is more important than the advantages of duck typing depends on the person you ask. – Maarten Bodewes Nov 01 '13 at 00:57
  • 7
    But this doesn't answer any of the questions: "However, how does Python know that the user of the function is passing in the proper types? Will the program just die if it's the wrong type, assuming the function actually uses the parameter? Do you have to specify the type?" or.. – qPCR4vir Mar 04 '16 at 11:28
  • 7
    @qPCR4vir, any object can be passed as an argument. The error (an exception, the program won't "die" if it's coded to catch it, see `try`/`except`) will occur when and if an operation is attempted that the object doesn't support. In Python 3.5 you can now optionally "specify types" of arguments, but no error occurs, per se, if the spec's violated; the typing notation is only meant to help separate tools that perform analysis etc, it does not alter the behavior of Python itself. – Alex Martelli Mar 04 '16 at 17:45
  • 2
    @AlexMartelli. Thank! For me this is the right answer: "The error (an exception, the program won't "die" if it's coded to catch it, see try/except) .." – qPCR4vir May 12 '16 at 16:12
  • 1
    @MaartenBodewes : It's not that easy. Opinions and opinions are two different matters. Meaning: It depends what your opinion is based on. Is it just a matter of taste? Or do you have facts or - like here - clearly defined criteria you base your opinion on? Programming languages should be designed (and should give priority to design aspects) in such a way that they help avoiding programming errors. If we use that as a criterion Python indeed "is not as good as static typing". And I think it would be quite unwise to dismiss this criterion. As well as another criterion: Assist the programmer. – Regis May May 20 '18 at 21:29
  • 1
    @RegisMay I'm basically rooted in security. And one of the ways of making a program secure is based on reducing possible state: the fewer possibilities there are given a certain position (in time) in an application, the easier it is to prove (or at least convince yourself) that it is secure. Python leaves too many options open because it doesn't specify the type of the arguments. A function may have a lot of possible outcomes or trigger state changes for each arg type. It's not for nothing that e.g. Java now introduces `var` for local variables but stays away from `var` for function arguments. – Maarten Bodewes May 20 '18 at 21:41
  • @RegisMay Maybe if all else were equal—but it isn't. It's very hard to design a static type system that's anywhere near as strong as Python's dynamic type system. C++, Java, Go, C#, etc. all have type systems that are much weaker, and also much leaker. (And the same is true for Python's own optional static type system in 3.5+.) Haskell and a few other functional languages are exceptions. Rust and maybe Swift are good compromises. But most of the static languages that people use looking for type safety are actually unsafe while at the same time overly restrictive. And that's not worth it. – abarnert Jun 23 '18 at 08:04
  • 2
    @MaartenBodewes Static typing in most languages gives you a false sense of security: "I made the compiler happy, so it must be correct…" and then you spend the weekend fixing null pointer exceptions. With a type system that's strong enough to catch the actual errors people actually make (again, that's mostly true for Haskell, and largely true for Rust and maybe Swift, and not at all true for C++, Java, Go, etc.), static typing can make code more secure. With a type system where half your program is dynamic casts, it's just fooling you into not realizing how insecure your code is. – abarnert Jun 23 '18 at 08:12
  • @abarnert Sounds strange to me: About 95% of all programming errors I see during development result in the lack strictness in types. This is what slows development to a great extent. This is what causes bugs if programs grow larger. So I'm not at all convinced that type strictness is something "that's not worth it". And without further information provided buy you I find it hard to believe that something like Java this should be unsafe because of having strictly typed variables. But maybe you expressed something else and I simply misunderstood you. I'd love to hear a more detailed explanation. – Regis May Jun 23 '18 at 22:56
  • 1
    @RegisMay Now you're mixing up not just strong and static typing, but strict typing as well. Java's type system is not strict because you can, and regularly do, cast things to Object and back (which does not happen in Haskell, or Python). And it doesn't have any mechanism to isolate the code that needs to break type safety (unlike Rust). That means it's not strict. And it's not strong, because there's no way to represent, e.g., something that's either an X or a Y (except by casting it to Object), much less more complex abstract data types. – abarnert Jun 24 '18 at 01:09
  • @RegisMay Also, do you seriously believe that Java code has fewer bugs than code in other languages? In most studies, even if you count bugs per line (which favors overly verbose languages like Java), Java still comes in the lower part of the heap. – abarnert Jun 24 '18 at 01:11
  • 1
    @abarnert First: That's not what I said. I said that during development most of all errors I see is type related. Data of wrong type is passed to functions and methods and similar things. And it's perfectly clear why: a) There is no compiler that could check that and b) there is no accurate type inference possible by IDEs. These tasks a compiler would do here has to be done manually during development and during extensive tests. And second: As we all know how "well" tested standard software tends to be I find it quite implausible that Python software should be of better quality than Java softw – Regis May Jun 24 '18 at 09:59
  • @abarnert Thank's for point out the difference between strong/static/strict typing. Probably we both talk about completely different things. – Regis May Jun 24 '18 at 10:02
  • 1
    @RegisMay The answer already pointed out the difference between strong and static typing. And if most of the errors you make during coding are the kind of simple type errors caught by Java static typing, then Mypy would catch them just as well with Python. But I don't believe that's really true in the first place, and I find it particularly bizarre that nearly all the people who insist that it is true have no interest in using Mypy. – abarnert Jun 24 '18 at 18:32
  • @RegisMay Meanwhile, there's been research since Hatton's 1997 "Follies and Fallacies" (which argued that defects/kLOC was constant across languages), and there's no obvious connection in any papers between defect rates and dynamic typing. In particular, JavaScript tends to do even worse than Java, while Python is about average, and Lisp is better than average. You can argue about how to interpret these studies, or challenge them, but just ignoring all data so you can insist on your preconceptions is a different story. – abarnert Jun 24 '18 at 18:42
  • 1
    erb's answer further down the answer list is worth a read for Python 3 users: https://stackoverflow.com/a/21384492/5699807 – Priyank Oct 01 '18 at 18:58
  • I was prompted by SO to give feedback. I think this answer is outdated, since Python 3 now supports type declaration for function arguments. The [top voted answer to this question](https://stackoverflow.com/a/21384492/543738) explains it better. – Mr. Lance E Sloan Nov 14 '18 at 19:12
  • @LS that is not a type declaration, that is a type hint. This answer is absolutely not outdated, and indeed, is completely accurate even given type hinting. – juanpa.arrivillaga Jul 23 '19 at 21:38
  • 1
    late to discussion. I am completely with @Regis May on the topic of strict typing. no easy way to define type for function parameter, return types. – Vortex Mar 02 '21 at 04:38
22

You don't specify a type. The method will only fail (at runtime) if it tries to access attributes that are not defined on the parameters that are passed in.

So this simple function:

def no_op(param1, param2):
    pass

... will not fail no matter what two args are passed in.

However, this function:

def call_quack(param1, param2):
    param1.quack()
    param2.quack()

... will fail at runtime if param1 and param2 do not both have callable attributes named quack.

TM.
  • 108,298
  • 33
  • 122
  • 127
  • 1
    +1: The attributes and methods are not determined statically. The concept of how would this "proper type" or "wrong type" are established by whether or not the type works properly in the function. – S.Lott Mar 22 '10 at 15:09
15

I have implemented a wrapper if anyone would like to specify variable types.

import functools
    
def type_check(func):

    @functools.wraps(func)
    def check(*args, **kwargs):
        for i in range(len(args)):
            v = args[i]
            v_name = list(func.__annotations__.keys())[i]
            v_type = list(func.__annotations__.values())[i]
            error_msg = 'Variable `' + str(v_name) + '` should be type ('
            error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
            if not isinstance(v, v_type):
                raise TypeError(error_msg)

        result = func(*args, **kwargs)
        v = result
        v_name = 'return'
        v_type = func.__annotations__['return']
        error_msg = 'Variable `' + str(v_name) + '` should be type ('
        error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
        if not isinstance(v, v_type):
                raise TypeError(error_msg)
        return result

    return check

Use it as:

@type_check
def test(name : str) -> float:
    return 3.0

@type_check
def test2(name : str) -> str:
    return 3.0

>> test('asd')
>> 3.0

>> test(42)
>> TypeError: Variable `name` should be type (<class 'str'>) but instead is type (<class 'int'>)

>> test2('asd')
>> TypeError: Variable `return` should be type (<class 'str'>) but instead is type (<class 'float'>)

EDIT

The code above does not work if any of the arguments' (or return's) type is not declared. The following edit can help, on the other hand, it only works for kwargs and does not check args.

def type_check(func):

    @functools.wraps(func)
    def check(*args, **kwargs):
        for name, value in kwargs.items():
            v = value
            v_name = name
            if name not in func.__annotations__:
                continue
                
            v_type = func.__annotations__[name]

            error_msg = 'Variable `' + str(v_name) + '` should be type ('
            error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ') '
            if not isinstance(v, v_type):
                raise TypeError(error_msg)

        result = func(*args, **kwargs)
        if 'return' in func.__annotations__:
            v = result
            v_name = 'return'
            v_type = func.__annotations__['return']
            error_msg = 'Variable `' + str(v_name) + '` should be type ('
            error_msg += str(v_type) + ') but instead is type (' + str(type(v)) + ')'
            if not isinstance(v, v_type):
                    raise TypeError(error_msg)
        return result

    return check

Community
  • 1
  • 1
Gergely Papp
  • 800
  • 1
  • 7
  • 12
14

Many languages have variables, which are of a specific type and have a value. Python does not have variables; it has objects, and you use names to refer to these objects.

In other languages, when you say:

a = 1

then a (typically integer) variable changes its contents to the value 1.

In Python,

a = 1

means “use the name a to refer to the object 1”. You can do the following in an interactive Python session:

>>> type(1)
<type 'int'>

The function type is called with the object 1; since every object knows its type, it's easy for type to find out said type and return it.

Likewise, whenever you define a function

def funcname(param1, param2):

the function receives two objects, and names them param1 and param2, regardless of their types. If you want to make sure the objects received are of a specific type, code your function as if they are of the needed type(s) and catch the exceptions that are thrown if they aren't. The exceptions thrown are typically TypeError (you used an invalid operation) and AttributeError (you tried to access an inexistent member (methods are members too) ).

tzot
  • 92,761
  • 29
  • 141
  • 204
  • Python has variables: they just aren't named locations in memory that store values, rather they are names that refer to values. – chepner May 19 '23 at 14:16
  • “…and you use names to refer to these objects”, yes. – tzot May 24 '23 at 16:56
9

Python is not strongly typed in the sense of static or compile-time type checking.

Most Python code falls under so-called "Duck Typing" -- for example, you look for a method read on an object -- you don't care if the object is a file on disk or a socket, you just want to read N bytes from it.

Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
  • 25
    Python _is_ strongly typed. It is also dynamically typed. – Daniel Newby Mar 22 '10 at 07:13
  • 2
    But this doesn't answer any of the questions: "However, how does Python know that the user of the function is passing in the proper types? Will the program just die if it's the wrong type, assuming the function actually uses the parameter? Do you have to specify the type?" or.. – qPCR4vir Mar 04 '16 at 11:30
8

As Alex Martelli explains,

The normal, Pythonic, preferred solution is almost invariably "duck typing": try using the argument as if it was of a certain desired type, do it in a try/except statement catching all exceptions that could arise if the argument was not in fact of that type (or any other type nicely duck-mimicking it;-), and in the except clause, try something else (using the argument "as if" it was of some other type).

Read the rest of his post for helpful information.

Community
  • 1
  • 1
Nick Presta
  • 28,134
  • 6
  • 57
  • 76
5

Python doesn't care what you pass in to its functions. When you call my_func(a,b), the param1 and param2 variables will then hold the values of a and b. Python doesn't know that you are calling the function with the proper types, and expects the programmer to take care of that. If your function will be called with different types of parameters, you can wrap code accessing them with try/except blocks and evaluate the parameters in whatever way you want.

Brannon
  • 5,324
  • 4
  • 35
  • 83
Kyle
  • 443
  • 3
  • 11
  • 13
    Python does not have variables, like other languages where variables have a type and a value; it has *names* pointing to *objects*, which know their type. – tzot Apr 02 '10 at 21:49
2

You never specify the type; Python has the concept of duck typing; basically the code that processes the parameters will make certain assumptions about them - perhaps by calling certain methods that a parameter is expected to implement. If the parameter is of the wrong type, then an exception will be thrown.

In general it is up to your code to ensure that you are passing around objects of the proper type - there is no compiler to enforce this ahead of time.

Justin Ethier
  • 131,333
  • 52
  • 229
  • 284
2

There's one notorious exception from the duck-typing worth mentioning on this page.

When str function calls __str__ class method it subtly сhecks its type:

>>> class A(object):
...     def __str__(self):
...         return 'a','b'
...
>>> a = A()
>>> print a.__str__()
('a', 'b')
>>> print str(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __str__ returned non-string (type tuple)

As if Guido hints us which exception should a program raise if it encounters an unexpected type.

Antony Hatchkins
  • 31,947
  • 10
  • 111
  • 111
1

In Python everything has a type. A Python function will do anything it is asked to do if the type of arguments support it.

Example: foo will add everything that can be __add__ed ;) without worrying much about its type. So that means, to avoid failure, you should provide only those things that support addition.

def foo(a,b):
    return a + b

class Bar(object):
    pass

class Zoo(object):
    def __add__(self, other):
        return 'zoom'

if __name__=='__main__':
    print foo(1, 2)
    print foo('james', 'bond')
    print foo(Zoo(), Zoo())
    print foo(Bar(), Bar()) # Should fail
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pratik Deoghare
  • 35,497
  • 30
  • 100
  • 146
1

I didn't see this mentioned in other answers, so I'll add this to the pot.

As others have said, Python doesn't enforce type on function or method parameters. It is assumed that you know what you're doing, and that if you really need to know the type of something that was passed in, you will check it and decide what to do for yourself.

One of the main tools for doing this is the isinstance() function.

For example, if I write a method that expects to get raw binary text data, rather than the normal utf-8 encoded strings, I could check the type of the parameters on the way in and either adapt to what I find, or raise an exception to refuse.

def process(data):
    if not isinstance(data, bytes) and not isinstance(data, bytearray):
        raise TypeError('Invalid type: data must be a byte string or bytearray, not %r' % type(data))
    # Do more stuff

Python also provides all kinds of tools to dig into objects. If you're brave, you can even use importlib to create your own objects of arbitrary classes, on the fly. I did this to recreate objects from JSON data. Such a thing would be a nightmare in a static language like C++.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
1

To effectively use the typing module (new in Python 3.5) include all (*).

from typing import *

And you will be ready to use:

List, Tuple, Set, Map - for list, tuple, set and map respectively.
Iterable - useful for generators.
Any - when it could be anything.
Union - when it could be anything within a specified set of types, as opposed to Any.
Optional - when it might be None. Shorthand for Union[T, None].
TypeVar - used with generics.
Callable - used primarily for functions, but could be used for other callables.

However, still you can use type names like int, list, dict,...

prosti
  • 42,291
  • 14
  • 186
  • 151
1

Whether you specify a type hint or not, things will fail at run time.

However, you can provide type hints for both function arguments and its return type. For example, def foo(bar: str) -> List[float] hints that bar is expected to be a string and the function returns a list of float values. This will result in a type check error when the method is invoked if the types don't match (before the use of the parameter in the function, or of the return type). This IMOHO is much more helpful in catching such errors vs an error about a missing field or method somewhere in the method call. I recommend reading the official Python documentation Typing - Support for type hints.

Also, if you use type hints, you can use static type checkers to verify code correctness. One such tool that's built into python is Mypy (official documentation). This section of an article on Static Type Checking gives a very good intro on how to use it.

sprite
  • 3,724
  • 3
  • 28
  • 30
  • 1
    def foo(bar: string) -> list[float]: Instead of "string" seems need "str", but even still list[float] gives error "TypeError: 'type' object is not subscriptable " So def foo(bar: str) -> list: would work , but do not know how to do with list of floats – Alexander Chervov Jun 01 '22 at 09:17
  • 1
    @AlexanderChervov thanks for pointing out that typo, I fixed it. Also, you **can** set the return type to be a list of float values. I had a nother typo there, it should be `List[float]`, with a capital L, also fixed. Try it again and see it works. – sprite Jun 02 '22 at 05:52
  • `List` is only required prior to Python 3.9, and is now deprecated in favor of `list`. – chepner May 19 '23 at 14:26