This is a follow-up PR of #169045 and the second part of #179086. In #179086, we added support for defining regions in Python-defined ops, but its usefulness was quite limited because we still couldn’t mark an op as a `Terminator` or `NoTerminator`. In this PR, we port the `DynamicOpTrait` (introduced on the C++ side for `DynamicDialect` in #177735) to Python, so we can dynamically attach traits to Python-defined ops.