Neither is better than the other, they do exactly the same thing. However, using .encode() and .decode() is the more common way to do it. It is also compatible with Python 2.

Answer from Lennart Regebro on Stack Overflow
🌐
The Python Coding Stack
thepythoncodingstack.com › the python coding stack › `bytes`: the lesser-known python built-in sequence • and understanding utf-8 encoding
`bytes`: The Lesser-Known Python Built-In Sequence • And Understanding UTF-8 Encoding
July 15, 2025 - You can get a free trial at The Python Coding Place and you also get access to a members-only forum. ... So, back to 1's and 0's. The accented letter é is represented by two bytes. The value of the first of these bytes is c3 in hexadecimal, which is 195 in decimal. Let's convert this to binary: ... Here's how to understand a byte in a UTF-8 encoded number.
Top answer
1 of 3
74

Neither is better than the other, they do exactly the same thing. However, using .encode() and .decode() is the more common way to do it. It is also compatible with Python 2.

2 of 3
19

To add to Lennart Regebro's answer There is even the third way that can be used:

encoded3 = str.encode(original, 'utf-8')
print(encoded3)

Anyway, it is actually exactly the same as the first approach. It may also look that the second way is a syntactic sugar for the third approach.


A programming language is a means to express abstract ideas formally, to be executed by the machine. A programming language is considered good if it contains constructs that one needs. Python is a hybrid language -- i.e. more natural and more versatile than pure OO or pure procedural languages. Sometimes functions are more appropriate than the object methods, sometimes the reverse is true. It depends on mental picture of the solved problem.

Anyway, the feature mentioned in the question is probably a by-product of the language implementation/design. In my opinion, this is a nice example that show the alternative thinking about technically the same thing.

In other words, calling an object method means thinking in terms "let the object gives me the wanted result". Calling a function as the alternative means "let the outer code processes the passed argument and extracts the wanted value".

The first approach emphasizes the ability of the object to do the task on its own, the second approach emphasizes the ability of an separate algoritm to extract the data. Sometimes, the separate code may be that much special that it is not wise to add it as a general method to the class of the object.

🌐
Real Python
realpython.com › python-bytes
Bytes Objects: Handling Binary Data in Python – Real Python
March 5, 2025 - Python only understands unsigned bytes, but there are ways to emulate signed bytes should you need to—more on that later. Note: You could theoretically represent a floating-point number on a single byte by encoding the sign, exponent, and mantissa using fewer bits.
🌐
GeeksforGeeks
geeksforgeeks.org › python › python-bytes-method
Python bytes() method - GeeksforGeeks
July 11, 2025 - bytes() method in Python is used to create a sequence of bytes. In this article, we will check How bytes() methods works in Python. ... A string will be encoded into bytes using the specified encoding (default is 'utf-8').
🌐
freeCodeCamp
freecodecamp.org › news › python-bytes-to-string-how-to-convert-a-bytestring
Python Bytes to String – How to Convert a Bytestring
April 10, 2023 - In this example, we define a byte string b"Hello, world!" and use the str() constructor to convert it to a string object. We specify the encoding format as utf-8 using the encoding parameter. Finally, we print the resulting string to the console. We can also use the bytes() constructor, a built-in Python function used to create a new bytes object.
🌐
Programiz
programiz.com › python-programming › methods › built-in › bytes
Python bytes()
# convert string to bytes byte_message = bytes(message, 'utf-8') print(byte_message) # Output: b'Python is fun' ... bytes() method returns a bytes object which is an immutable (cannot be modified) sequence of integers in the range 0 <=x < 256. If you want to use the mutable version, use the ...
🌐
Mimo
mimo.org › glossary › python › bytes
Python Bytes: Syntax, Usage, and Examples
This process is essential when sending data over a network or storing data in binary files. The opposite of this operation—bytes to string—requires decoding. If you skip specifying the encoding, Python 3 uses UTF-8 as the default encoding, which works for most unicode characters.
Find elsewhere
🌐
Reddit
reddit.com › r/learnpython › why is encoding bytes to utf-8 called decoding?
r/learnpython on Reddit: Why is encoding bytes to UTF-8 called decoding?
March 30, 2022 -

This always confuses me every single time. I remember it by doing the opposite of what I intuitively think it should be. Perhaps I'm the only one?

bytes.decode - encodes the bytes as UTF-8

str.encode - decodes "removes" the UTF-8 encoding

There is also the abbreviation BADTIE: Bytes Are Decoded, Text Is Encoded. To help to remember it.

Top answer
1 of 4
25
You are indeed confused: bytes.decode - encodes the bytes as UTF-8 No, it decodes a byte sequence to a string, interpreting it as an UTF-8 encoded string. Strings are a sequence of code points (or characters) in Unicode. Unicode and UTF-8 are not the same thing! UTF-8 is the most popular, but still only one of many encoding schemes for Unicode. Take the string ABCÄĀ. This is represented by the unicode code point sequence U+0041 U+0042 U+0043 U+00c4 U+0100. As you see, unicode code points are more than 8 bits, so we need a way to store them in 8-bit byte sequences. One way to do that is to use UTF-8, which turns it into the byte sequence 0x41 0x42 0x43 0xc3 0x84 0xc4 0x80. As another example, UTF-16 would encode this as 0xff 0xfe 0x00 0x41 0x00 0x42 0x00 0x43 0x00 0xc4 0x01 0x00. Notice that except for the 0xff 0xfe BOM (Byte Order Mark) at the beginning, it's simply storing the 16 bit value of the code point in sequence (code points can have more than 16 bits, so it only works this way below 32768, similar to how UTF-8 doesn't change the representation below 128). The BOM is needed to know which of the two bytes represents the most significant bits, as in, is 0x12 0x34 representing 0x1234 or 0x3412. (Some machines store 16 bit values in the first way ("big endian"), others, like x86, in the other ("little endian")) There are also other encoding schemes, for example, if every unicode code point is below 256, you can use latin-1, which will just use the lower 8 bits of the unicode code point. To summarize: bytes.decode decodes a byte sequence into a string, which is an abstract representation of code points. str.encode turns this abstract representation into a byte sequence.
2 of 4
11
Because in Python 3 the strings are in unicode by default, and bytes are an optional encoding, if you want to deal with things on that level.
Top answer
1 of 1
2

bytes

You are working with bytes which is very cumbersome.

  • You have to prefix all literals
  • single bytes have type int and need to be converted to to bytes again At least I cannot do this without debugging some errors.

It is much more convenient to work with strings. So I suggest to decode the bytes to a string with an 8-bit codec like 'latin-1'. No more bytes([x]) or x.to_bytes(). You can use plain comprehension, plain ''.join(), etc. If required you encode the string to bytes again with the very same codec.

dict.get()

For conditional translations there is a nice trick - dict.get() - which allows to pass a default value for non-existing keys.

translated = replacements.get(char, char)

This returns replacements[char] if char is in replacements.keys(). Otherwise it returns the default char. To use this functionality you include the escape character in the translation table (however you fill it).

replacements = {escape_char: escape_char + escape_char,
       '\n':   escape_char + '\x00',
       '\xfd': escape_char + '\x01',
       '\xfe': escape_char + '\x02',
       '\xff': escape_char + '\x03',
       }

The encoding function now reads

def encode(data: bytes) -> bytes:
    str_data = data.decode(encoding='latin-1')
    str_enc = ''.join(replacements.get(x, x) for x in str_data)
    return str_enc.encode(encoding='latin-1')

loop like a pro

In your decoding function you loop over len(data) which is an anti-pattern. You should always loop over the elements. To make it worse you use a while loop and increment the loop counter manually. You also access elements with index i+1 which again is error prone. Loop over elements only and keep a little state like a previous_byte. You cannot be out of bounds.

def decode(data: bytes) -> bytes:
    d_data = data.decode(encoding='latin-1')
    l = []
    esc = ''
    for x in d_data:
        if not esc and x == escape_char:
            esc = x
            continue
        l.append(reverse_replacements.get(esc + x, esc + x))
        esc = ''
    assert esc == ''
    return ''.join(l).encode(encoding='latin-1)')

testing

For your nicely testable functions you should provide some tests. There are unit test frameworks available. You can also do the most primitive assertions, of course in a main guard.

if __name__ == '__main__':
    assert encode(b"asdf") == b"asdf"
    assert decode(b"asdf") == b"asdf"

    assert encode(b"as\ndf") == b"as=\x00df"
    assert decode(b"as=\x00df") == b"as\ndf"
    assert encode(b"\ndf") == b"=\x00df"
    assert decode(b"=\x00df") == b"\ndf"
    assert encode(b"as\n") == b"as=\x00"
    assert decode(b"as=\x00") == b"as\n"

    assert encode(b"as\xfddf") == b"as=\x01df"
    assert decode(b"as=\x01df") == b"as\xfddf"

    assert encode(b"as\xfedf") == b"as=\x02df"
    assert decode(b"as=\x02df") == b"as\xfedf"

    assert encode(b"as\xffdf") == b"as=\x03df"
    assert decode(b"as=\x03df") == b"as\xffdf"
🌐
DigitalOcean
digitalocean.com › community › tutorials › python-string-encode-decode
Python String Encode and Decode: Complete Guide | DigitalOcean
Master Python string encode() and decode() methods. Learn UTF-8, ASCII, Unicode handling, error handling modes, and practical encoding/decoding examples.
🌐
Python
docs.python.org › 3 › library › codecs.html
codecs — Codec registry and base classes
This module defines base classes for standard Python codecs (encoders and decoders) and provides access to the internal Python codec registry, which manages the codec and error handling lookup process. Most standard codecs are text encodings, which encode text to bytes (and decode bytes to text), but there are also codecs provided that encode text to text, and bytes to bytes.
🌐
Python documentation
docs.python.org › 3 › howto › unicode.html
Unicode HOWTO — Python 3.14.7 documentation
UTF stands for “Unicode Transformation Format”, and the ‘8’ means that 8-bit values are used in the encoding. (There are also UTF-16 and UTF-32 encodings, but they are less frequently used than UTF-8.) UTF-8 uses the following rules: If the code point is < 128, it’s represented by the corresponding byte value.
🌐
GeeksforGeeks
geeksforgeeks.org › byte-objects-vs-string-python
Byte Objects vs String in Python - GeeksforGeeks
November 28, 2023 - Converting Strings to byte objects is termed as encoding. This is necessary so that the text can be stored on disk using mapping using ASCII or UTF-8 encoding techniques. This task is achieved using encode(). It take encoding technique as argument.
🌐
Python
docs.python.org › 3 › builtins › stdtypes.html
Built-in Types — Python 3.14.7 documentation
>>> encoded_str_to_bytes = 'Python'.encode() >>> type(encoded_str_to_bytes) <class 'bytes'> >>> encoded_str_to_bytes b'Python'
🌐
DataCamp
datacamp.com › tutorial › bytes-to-string-python
How to Convert Bytes to String in Python | DataCamp
June 12, 2024 - In this example, UTF-8 decoding is used. UTF-8 is the default encoding, so it's not required in the example above, but we can also use other encodings. Python has a built-in bytes data structure, which is an immutable sequence of integers in the range 0 to 255.
🌐
Real Python
realpython.com › convert-python-bytes-to-strings
How to Convert Bytes to Strings in Python – Real Python
July 18, 2026 - You’re bound to meet some very confused Russians. If no encoding is specified, Python defaults to UTF-8, which is the standard encoding for almost all modern systems and web data.
🌐
Net Informations
net-informations.com › python › iq › byte.htm
How to convert bytes to string in Python?
When you convert a bytes object ... or "stringifying" the bytes object. In Python, the default encoding used for decoding bytes objects to strings is UTF-8....
🌐
DataCamp
datacamp.com › tutorial › string-to-bytes-conversion
How to Convert String to Bytes in Python | DataCamp
June 5, 2024 - In Python, use the .encode() method on a string to convert it into bytes, optionally specifying the desired encoding (UTF-8 by default).
🌐
Real Python
realpython.com › python-encodings-guide
Unicode & Character Encodings in Python: A Painless Guide – Real Python
May 20, 2019 - Python 3 source code is assumed to be UTF-8 by default. This means that you don’t need # -*- coding: UTF-8 -*- at the top of .py files in Python 3. All text (str) is Unicode by default. Encoded Unicode text is represented as binary data (bytes).