8

Can I indicate a specific dictionary shape/form for an argument to a function in python?

Like in typescript I'd indicate that the info argument should be an object with a string name and a number age:

function parseInfo(info: {name: string, age: number}) { /* ... */ }

Is there a way to do this with a python function that's otherwise:

def parseInfo(info: dict):
  # function body

Or is that perhaps not Pythonic and I should use named keywords or something like that?

Jonathan Tuzman
  • 11,568
  • 18
  • 69
  • 129
  • 1
    Also: [Python 3 dictionary with known keys typing](https://stackoverflow.com/q/44225788/7851470), [Is it possible to “hint” dictionary keys?](https://stackoverflow.com/q/56198483/7851470), and [Python type hint for specific values of dictionary](https://stackoverflow.com/q/53074889/7851470). – Georgy Nov 22 '20 at 12:49

2 Answers2

18

In Python 3.8+ you could use the alternative syntax to create a TypedDict:

from typing import TypedDict

Info = TypedDict('Info', {'name': str, 'age': int})


def parse_info(info: Info):
    pass

From the documentation on TypedDict:

TypedDict declares a dictionary type that expects all of its instances to have a certain set of keys, where each key is associated with a value of a consistent type. This expectation is not checked at runtime but is only enforced by type checkers.

Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76
  • Thank you, this is the actual correct simple answer. Now if I access fields not in the type definition, I get a warning. However, I notice that when I *call* `parse_info` with an incorrect dict, I don't see any errors or warnings (in PyCharm). Is this the expected behavior? – Jonathan Tuzman Nov 23 '20 at 15:04
  • 1
    @JonathanTuzman This will depend on how PyCharm is doing the type checking, perhaps you can search if that is an issue on their side. There seems to be some [issues](https://youtrack.jetbrains.com/issue/PY-36008) with TypedDict and autocompletion, but I cannot tell for sure if your case fits one of the issues. – Dani Mesejo Nov 23 '20 at 15:14
0

Perhaps you could do the following:

def assertTypes(obj, type_obj):
    for t in type_obj:
        if not(t in obj and type(obj[t]) == type_obj[t]):
            return False
    return True

def parseInfo(info):
    if not assertTypes(info, {"name": str, "age": int}):
        print("INVALID OBJECT FORMAT")
        return
    #continue

>>> parseInfo({"name": "AJ", "age": 8})
>>> parseInfo({"name": "AJ", "age": 'hi'})
INVALID OBJECT FORMAT
>>> parseInfo({"name": "AJ"})
INVALID OBJECT FORMAT
>>> parseInfo({"name": 1, "age": 100})
INVALID OBJECT FORMAT
>>> parseInfo({"name": "Donald", "age": 100})
>>> 
A.J. Uppal
  • 19,117
  • 6
  • 45
  • 76