There is no such distinction between compile time and run time in Python; everything can be done dynamically. Want to use a class with slots computed at runtime? No problem:
>>> def with_slots(**kwargs):
... class SlottedClass(object):
... __slots__ = kwargs.keys()
... def __init__(self, **kwargs):
... for k, v in kwargs.items():
... setattr(self, k, v)
... return SlottedClass(**kwargs)
...
>>> with_slots(foo=1, bar='qux').zork = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'SlottedClass' object has no attribute 'zork'
Of course there are many improvements that could be made to this approach (e.g. not creating a new class for each instance, or setting the __name__ to something meaningful) but I hope it shows that dynamic typing and robust error checking aren't contradictory.