python-3.xvariablestransformf-stringoctal

why is transforming a string to octal value \ooo isnt working when i use an f-string and a variable inside {}?


When using a backslash followed by three integers, Python interprets them as an octal value:

# A backslash followed by three integers will result in an octal value:
txt = "\110\145\154\154\157"
print(txt)  # Output: Hello

However, when I try to replace the octal sequence with a variable inside {} using an f-string, like this:

# Attempting to use a variable inside an f-string:
x = 110
txt = f"\\{x}\145\154\154\157"
print(txt)

The output is \110ello instead of Hello.

Why does Python treat the variable x differently inside the f-string? How can I correctly substitute a variable into an octal escape sequence?


Solution

  • A few issues:

    In the f-string you should put the dynamic part within braces so that its evaluation is the final substring you want to get. In this case that means the braces should have the expression that evaluates to the character "H".

    Here is how you could do that:

    x = 0o110  # Define as the intended number using octal notation
    s = f"{chr(x)}\145\154\154\157" # turn that number into a character
    print(s)
    

    About dynamic octal numbers

    Realise that octal, hexadecimal, binary, ...etc, are notations of integers. Once you store the value that is intended by that notation, you just have the integer, not the notation. There is nothing "octally" about the value of x in the above code. Once that value is assigned, it is what we would naturally call 72, because we mostly speak "decimal", but it would also be right to say it is 110 in octal base, or 1001000 in binary or 48 in hexadecimal notation. You can "talk" about the value of x in any base you want, but it doesn't affect the value itself.

    So... if you want for instance to generate a random number, then you can set a range to choose from using octal representations for the boundary values, but the value that x will get has no "memory" of which base you used to express those values.

    You can do:

    x = random.randint(0o100, 0x150)
    

    But the following program would be compiled/parsed into exactly the same byte code:

    x = random.randint(64, 104)  # using decimal base notation
    

    In short, 0o100 and 64 are exactly the same integer, and which of these notations you use in your code has no impact on the actual value that it evaluates to.

    A string of digits

    Another scenario arises when you receive a number (somehow, like via user input) in the form of a character string, where each character is a digit of that number. And then maybe it is also given that these characters should be interpreted as octal digits.

    # We get a string(!) from user input. This is not a numeric data type.
    octal_digits = input("give number with octal notation: ")
    # Then we define the base to use for interpreting these characters as a number:
    x = int(octal_digits, 8)  # we "parse" a character string as denoting an integer
    # x has no memory of the original digits; it is just the integer that we parsed from it
    print(f"the corresponding character is {chr(x)}")