Converting Integers to roman between 1 and 1000
You asked about optimization, which usually refers either to speed or memory
usage, neither of which apply in this case. Your code has negligible memory
cost. And if we truly cared about the ability to convert large volumes of
numbers at high speed, we would pre-compute a dict mapping every decimal number
of interest to its Roman equivalent.
If I were you, I would focus on optimizing this problem for simplicity of
understanding and code readability. Your current code appears to be fine, and
one can definitely figure it out, but it still requires some puzzling over. The
classic way that I've seen people solve this problem more simply is to build up
the Roman numeral by subtracting away even multiples of each divisor in your
dict mapping divisors to Roman symbols. The trick is to proceed from largest
divisor to smallest. Since Python 3 preserves order in dicts, we can
achieve that simply by reversing the order in which the data is set up.
The basic idea is illustrated below.
def decimal_to_roman(num):
    # The Romans had &quot;integers&quot; too, so this seems like a more
    # accurate function name.

    # You should add a check here to validate that the given decimal
    # number falls within the supported ranged.

    # Relies on Python 3 having ordered dicts.
    divisors = {
        1000: 'M',
        900: 'CM',
        500: 'D',
        400: 'CD',
        100: 'C',
        90: 'XC',
        50: 'L',
        40: 'XL',
        10: 'X',
        9: 'IX',
        5: 'V',
        4: 'IV',
        1: 'I',
    }

    # Build up the Roman numeral by subtracting multiples of
    # each divisor, proceeding from largest to smallest.
    roman = ''
    for div, sym in divisors.items():
        quotient = divmod(num, div)[0]
        roman += sym * quotient
        num -= div * quotient
        if num &lt; 1:
            break
    return roman

When deciding on names for functions, variables, and so forth, aim for accuracy
and clarity in situations where the additional context is needed for
readability. This is a case where slightly longer names are appropriate (for
example, decimal_to_roman, divisors, and quotient). But when the
surrounding context and nearby names provide sufficient clues, you can aim for
brevity to reduce the visual weight of the code -- which also helps with
readability, but from a different direction. For example, within the narrow
context of this function, the short names like num, div, and sym are
easily understood without giving them fully explicit names.
Finally, how do you know your code is correct? If you're trying to learn, you
could consider writing the corresponding roman_to_decimal() function and then
make sure your converters can round-trip the data correctly. Another approach
is to find one of the many Python functions floating around the internet and use
it as your checker. Or you can install the
roman Python package to check
everything, as shown here:
import roman

for n in range(1, 4000):
    exp = roman.toRoman(n)
    got = decimal_to_roman(n)
    if exp != got:
        print(n, exp, got)