In a word: Flexibility. I've used dynamic properties when needing to pass data with GUI objects to a method that handles an event attached to them as one example. I'm sure there are dozens of other uses.
That said, if you're the type of person that likes to have forced attributes and not allow new ones added dynamically you can do so. I can imagine a few places that it's useful, for example helping to ensure you're not setting the wrong value (typo) in your code. Something of a self check.
In order to restrict/prevent the addition of dynamic values you can add a __slots__
property to the class as exampled below.
class Foo(object):
__slots__ = ['val', 'val2']
pass
foo = Foo()
foo.val = 10
print(foo.val)
foo.b = 5 # throws exception
print(foo.b)