I'm looking for a way to create a type that indicates that a variable is an element of some other collection. I know of the Collection type:
from typing import Collection
Foo = Collection[Bar]
Instead, I'd like to do the inverse of that, i.e.
Bar = Element[Foo]
Is there a way to accomplish this?
The use-case I have in mind is to be able to something like:
import numpy as np
from gym.spaces import Space, Box, Discrete
Element = ... # some type definition
def func(x: Element[Box], i: Element[Discrete]) -> Element[Box]:
""" asserts are implied by the type annotations """
assert isinstance(x, np.ndarray)
assert isinstance(i, int)
return x * i
Here's a slightly more detailed example using gym.spaces
:
from gym.spaces import Space, Box, Discrete
box = Box(low=0, high=1, shape=(3,))
dsc = Discrete(5)
x = box.sample() # example: x = array([0.917, 0.021, 0.740], dtype=float32)
i = dsc.sample() # example: i = 3
def check(space: Space, y: Element[Space]) -> Element[Space]:
if y not in space:
raise ValueError("y not an element of space")
return y
x = check(box, x)
i = check(dsc, i)
How does this work for you?
I have written the type hints for
Space
,Discrete
andBox
as type stubs, such that you can add them if you don't controll the source ofgym.sources
. You should be able to add theshape
parameter toBox
pretty easily.The basic idea here is that we parametrize
Space
with the type of an element it can contain. We useNewType
in order to make the space elements subtypes of what they fundamentally are (an element sampled fromDiscrete
is anint
, and hasint
properties) without sacrificing the guaranteecheck
enforces in thaty
was sampled fromspace
.