outlaw
outlaw

Reputation: 241

Retrieve keys of output dictionary of python function without calling the function

assuming I have the following simple function

def f(name: str):
    print (name) 
    return {'x': 2, 'y': 1}

Assuming I have access to the function f and I want to retrieve the keys of the returned value dict, without calling the function itself, is there a way to achieve this?

EDIT: The expected output in this case is: ['x', 'y']

Upvotes: 0

Views: 68

Answers (2)

MisterMiyagi
MisterMiyagi

Reputation: 50116

TLDR: Fetch the values from f.__code__.co_consts[-1].

>>> f.__code__.co_consts[-1]
('x', 'y')

Literals are encoded in the code and constants of a function object. A convenient means to inspect this is to use the builtin dis module:

>>> import dis
>>> dis.dis(f)
  2           0 LOAD_GLOBAL              0 (print)
              2 LOAD_FAST                0 (name)
              4 CALL_FUNCTION            1
              6 POP_TOP

  3           8 LOAD_CONST               1 (2)
             10 LOAD_CONST               2 (1)
             12 LOAD_CONST               3 (('x', 'y'))
             14 BUILD_CONST_KEY_MAP      2
             16 RETURN_VALUE

As one can see, building a dict such as {'x': a, 'y': b, ...} will first load the individual values, then a single tuple of all keys. If nothing else is loaded before returning the dict, the keys are the last constant of the function.

The code and constants of a function object is accessible for programmatic inspection. Fetching "the last constant of the function" f literally translates to f.__code__.co_consts[-1].


Disclaimer: Extracting content from the code and constants of a function may be Python version dependent and especially depend on the function. Such an approach can be brittle and should not be used when arbitrary functions need to be processed.

Upvotes: 1

jonrsharpe
jonrsharpe

Reputation: 122062

Why not annotate the return type, using a TypedDict:

from typing import TypedDict


class F(TypedDict):
    x: int
    y: int


def f(name: str) -> F:
    print (name) 
    return {'x': 2, 'y': 1}

Not only does this make it easy to get the keys in the return dictionary from outside the function:

>>> f.__annotations__['return'].__annotations__.keys()
dict_keys(['x', 'y'])

it actually allows the type to be checked, both inside:

def f(name: str) -> F:
    return {'x': 2, 'y': 1}  # OK


def g(name: str) -> F:
    return {'x': 2, 'y': '1'}  # wrong type


def h(name: str) -> F:
    return {'x': 2, 'y': 1, 'z': 'foo'}  # extra key

and outside:

x: int = f('foo')['x']  # OK
y: str = f('foo')['y']  # wrong type
z: int = f('foo')['z']  # missing key

the function (see MyPy playground). Otherwise the inferred return type is just dict[str, int], which can't be checked so precisely, and you have to go spelunking into f.__code__ to find the keys.

Upvotes: 2

Related Questions