4

I have to work using binary formated numbers and I'm wondering if there's a simple and easy built in way to use them. I am aware of the bytearray but it works with byte type and it is absolutely not intuitive (at least for me).

So, is there any way of handling binary numbers (assign them to a variable, perform bit operations, transform to ASCII, etc.) easily? If not built in, at least a nice and understandable module.

Just in case what I'm asking is not clear enough, here is an example of what I picture a nice way for handling binary would be:

    bin_num1 = Binary('100')
    bin_num2 = Binary(0)

    print(bin_num1.and(bin_num2))    # Prints 000

I'm using Python 3.6 but any solution in any version would do the job.

Edit 1:

As pointed out in the comments, 0bXXX could work to get a type int from a binary just as bin() would do the opossite. Still, this would be working with integers, the result returned would be an integer and character conversion (e.g. bin('a')) would be an error and require further conversions (from str to int and then to binary).

Berbus
  • 160
  • 3
  • 20
  • 6
    Possible duplicate of [Binary numbers in Python](https://stackoverflow.com/questions/1523465/binary-numbers-in-python) – 0x01 Mar 16 '18 at 07:51
  • 4
    Like this: `0b0 & 0b100`? – Ubdus Samad Mar 16 '18 at 07:53
  • Yeah, you're both right. Actually the answer would probably be a combination of both. "`0b0 & 0b100`" like operations would return a type int which could be converted to binary using `bin( )`. I'm going to wait and see if someone proposes an answer that does the job and returns binary type as a result. – Berbus Mar 16 '18 at 08:01

3 Answers3

6

Assign binary numbers to a variable: You can use integer variables to hold the binary values. They can be created from the binary representation using the 0b prefix.

x = 0b110  # assigns the integer 6

Perform bit operations: The bit operations & (and), | (or), ^ (xor), ~ (not) can be used on integers and perform the respective binary operations.

x = 0b110
y = 0b011
z = x & y  # assigns the integer 0b010 (2)

Transform them to ASCII: To convert a number to ASCII you can use chr.

x = 0b1100001  # assigns 97
print(chr(x))  # prints 'a'

Transform from ASCII: If you use integers to represent the binary values you can use ord to convert ASCII values.

s = 'a'
x = ord(a)  # assigns the integer 0b1100001 (97)

Print integer in binary: An integer can be printed in binary using the string format method on the string "{0:b}".

x = 0b1100001
s = "{0:b}".format(x)
print(s)  # prints '1100001'

If you do not mind the 0b prefix you can also use bin.

x = 0b1100001
s = bin(x)
print(s)  # prints '0b1100001'

Read integer from binary string: The int function allows you to specify a base that is used when parsing strings.

x = int("1100001", 2)  # assigns the integer 0b1100001 (97)
pschill
  • 5,055
  • 1
  • 21
  • 42
1

You can subclass int and write a __new__ to parse desired input as a binary to the integer. I currently have char and a string with zeros and ones as supported.

You can now just use it as an integer with all its methods for binary operations. It only keeps converting to integer if you use these methods. Therefore you have to override all these methods with magic functions (the double underscores, or dunders) to keep returning your Binary class. This can be repetitive, but with some other python magic it can be done quite concise.

NB. I might have missed some dunders (intentially or not), this is a good reference if you want to know more: https://www.python-course.eu/python3_magic_methods.php

class Binary(int):

    def __new__(self, val):
        if type(val) is str:
            if len(val) > 1:
                val = int(val, 2)
            else:
                val = ord(val)
        return super().__new__(self, val)

    dunders_binary = "and rand add radd sub rsub mul rmul lshift rlshift " + \
            "rshift rrshift and rand or ror xor rxor"

    for dunder in ("__%s__" % s for s in dunders_binary.split()):
        locals()[dunder] = lambda self, other, __f=dunder: \
                self.__class__(getattr(super(), __f)(other))

    dunders_unary = "neg pos invert"

    for dunder in ("__%s__" % s for s in dunders_unary.split()):
        locals()[dunder] = lambda self, __f=dunder: \
                self.__class__(getattr(super(), __f)())

    def __repr__(self):
        return bin(self)

    def __str__(self):
        return bin(self)
Hielke Walinga
  • 2,677
  • 1
  • 17
  • 30
  • This is actually what I was originally thinking of. Thank you! – Berbus Mar 16 '18 at 11:22
  • If you make it the accepted answer, more people can see the Python magic. Although the current answer is the standard approach to using binary in Python. – Hielke Walinga Mar 16 '18 at 12:09
1

It is amusing to ponder how integral binary/bytes were to programmers of yesteryear. Today's 'tangential' programmers using Python can go really far without worrying too much about what is happening inside the computers. Assembler code? Nah!

I am new to Python and found it interesting that it does not support unsigned integers.

It might be useful for the OP to learn or recall that a hexadecimal byte is made up of two 'nibbles'.

The Python documentation contains a section "Bitwise Operations on Integer Types".

The following program might be of interest to the OP and others new Python programmers:

#--------*---------*---------*---------*---------*---------*---------*---------*
# Desc: bits and bytes and ascii
#--------*---------*---------*---------*---------*---------*---------*---------*
import sys

while True:

    for i in range(0, 16):
        print(i, i.bit_length())
    print(255, (255).bit_length())
    print(256, (256).bit_length())
    print((16).to_bytes(1, byteorder='big',signed=False))
    print((255).to_bytes(1, byteorder='big',signed=False))
    b = (255).to_bytes(1, byteorder='big',signed=False)
    print(b)
    print(b[0])
    print("\nAscii Table")
    for i in range(0, 256):
        b = (i).to_bytes(1, byteorder='big',signed=False)
        print(b[0], b, ord(b), chr(ord(b)))

    sys.exit()
CopyPasteIt
  • 532
  • 1
  • 8
  • 22