HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Beginnings of a base-conversion game in Python

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
conversionbeginningsgamepythonbase

Problem

I'm pretty very new to python, so I thought that I'd write a game to teach me python and better teach me how to think in hexadecimal and binary. I'm starting off by writing a few functions to convert between the different bases.

I'm looking for feedback on any better ways to implement these functions. There might be python libraries to do exactly this but I don't care. I can't think of a better way to check for the values A-F, and what they are in different bases, so if there are any better (prettier) ways to do it (I couldn't seem to find a switch/case in python, but maybe I didn't look hard enough).

As well as any possible better implementation, I'd also like input on naming and layout and whatnot.

Hex to binary function:

import re

def hexToBi(hexStr):
    hexRegex = "[A-Fa-f0-9]+"
    if not bool(re.match(hexRegex, hexStr)):
        return ""

    biStr = ""

    for c in hexStr:
        if c == '0':
            biStr += "0000 "
        elif c == '1':
            biStr += "0001 "
        elif c == '2':
            biStr += "0010 "
        elif c == '3':
            biStr += "0011 "
        elif c == '4':
            biStr += "0100 "
        elif c == '5':
            biStr += "0101 "
        elif c == '6':
            biStr += "0110 "
        elif c == '7':
            biStr += "0111 "
        elif c == '8':
            biStr += "1000 "
        elif c == '9':
            biStr += "1001 "
        elif c.upper() == 'A':
            biStr += "1010 "
        elif c.upper() == 'B':
            biStr += "1011 "
        elif c.upper() == 'C':
            biStr += "1100 "
        elif c.upper() == 'D':
            biStr += "1101 "
        elif c.upper() == 'E':
            biStr += "1110 "
        elif c.upper() == 'F':
            biStr += "1111 "

    return biStr


Binary to Hex function

```
def biToHex(biStr):
biSpRegex = "^([01]{4}[\s^\s])+$"
biRegex = "^([01]{4})+$"

if not bool(re.match(biSpRegex, biStr)):
biStr = biStr.replace("

Solution

There is a shorter way to implement these converter functions, with the help of the int, bin and hex built-in Python functions:

def hex2bin(hex_str):
    # note: int('...', 16) parses from string as a hexadecimal number
    #       for example: int('deadbeef', 16) -> 3735928559
    # note: bin(int_num) formats `int_num` to binary string format
    #       for example: bin(5) -> '0b101'
    bin_str = bin(int(hex_str, 16))[2:]
    return ' '.join(bin_str[i:i+4] for i in xrange(0, len(bin_str), 4))
    # ^^^ only to pass your assertions. I would prefer this simpler version:
    #return bin(int(hex_str, 16))[2:]

def bin2hex(bin_str):
    # note: int('...', 2) parses from string as a hexadecimal number
    #       for example: int('101', 2) -> 5
    # note: hex(int_num) formats `int_num` to hexadecimal string format
    #       for example: hex(3735928559) -> '0xdeadbeaf'
    return hex(int(bin_str.replace(' ', ''), 2))[2:].upper()
    # ^^^ only to pass your assertions. I would prefer this simpler version:
    #return hex(int(bin_str, 2))[2:]

def from_hex(hex_str):
    return int(hex_str, 16)

def from_bin(bin_str):
    return from_hex(bin2hex(bin_str))

assert (hex2bin("ABC09") == "1010 1011 1100 0000 1001"), "Logic fail, \"1010 1011 1100 0000 1001\" expected, " + hex2bin("ABC09") + " recieved"
# assert (hex2bin("QWER") == ""), "Logic fail, \"\" expected, " + hex2bin("QWER") + " recieved"
assert (bin2hex("1111 1111 0000") == "FF0"), "Logic fail, \"FF0\" expected, " + bin2hex("1111 1111 0000") + " recieved"
assert (bin2hex("1101 0000") == "D0"), "Logic fail, \"D0\" expected, " + bin2hex("1101 0000") + " recieved"
assert (from_hex("FAC") == 4012), "Logic fail, \"4012\" expected, " + from_hex("FAC")  + " recieved"
assert (from_bin("1101 0111") == 215), "Logic fail, \"215\" expected, " + from_bin("1101 0111") == 215 + " recieved"


In addition to formatting according to PEP8,
it's also recommended to use snake_case for function names and variable names.

If you notice I used 2 kinds of naming pattern:

  • hex2bin and bin2hex convert from string to string



  • Btw, using "bin" for binary instead of "bi" is also better in my opinion



  • from_hex and from_bin convert from string representations to integer



These are qualitatively different, that's why I separated them.

As I left in embedded comments, the implementations could be shorter and simpler if you could give up on some of your requirements.
The good thing about the simpler versions is that Python's built-in tools can work with them directly, without additional transformations like removing spaces from 1111 1111 0000.

Note that I left one of your assertions broken on purpose and commented out.
I think it's good to take a hint from the built-in tools and raise a ValueError if anyone tries to convert an invalid number.
This kind of harsh response to invalid input is usually a good thing,
and helps avoiding potentially nasty bugs.

Code review

On top of what others have already said, there are a couple of things that can be improved about your code worth pointing out.

def hexToBi(hexStr):
    hexRegex = "[A-Fa-f0-9]+"
    if not bool(re.match(hexRegex, hexStr)):
        return ""


Instead of compiling a regex every time the function is called,
it's better to compile it once and reuse.
Define the compiled version as a global constant, for example:

re_hex_format = re.compile(r"[A-Fa-f0-9]+")


And then in your function:

def hexToBi(hexStr):
    if not bool(re_hex_format.match(hexStr)):
        return ""


Also, use r"..." for strings that are regular expressions.
For more details, see this page in the docs.

Finally, no need to wrap the output of .match(...) calls in a bool, this works just as well:

if not re_hex_format.match(hexStr):
    return ""


Since in case of no match, you get a None which is falsy,
and in case of a match you get a match object which is truthy.
If you read through PEP8,
this treatment of "truth" is encouraged in Python.

About this:

re.match(r"^([01]{4})+$", biStr)):


Note that re.match applies the pattern at the beginning of the string,
so the ^ is implied. The $ is indeed necessary. From help(re.match):

match(pattern, string, flags=0)
    Try to apply the pattern at the start of the string, returning
    a match object, or None if no match was found.

Code Snippets

def hex2bin(hex_str):
    # note: int('...', 16) parses from string as a hexadecimal number
    #       for example: int('deadbeef', 16) -> 3735928559
    # note: bin(int_num) formats `int_num` to binary string format
    #       for example: bin(5) -> '0b101'
    bin_str = bin(int(hex_str, 16))[2:]
    return ' '.join(bin_str[i:i+4] for i in xrange(0, len(bin_str), 4))
    # ^^^ only to pass your assertions. I would prefer this simpler version:
    #return bin(int(hex_str, 16))[2:]


def bin2hex(bin_str):
    # note: int('...', 2) parses from string as a hexadecimal number
    #       for example: int('101', 2) -> 5
    # note: hex(int_num) formats `int_num` to hexadecimal string format
    #       for example: hex(3735928559) -> '0xdeadbeaf'
    return hex(int(bin_str.replace(' ', ''), 2))[2:].upper()
    # ^^^ only to pass your assertions. I would prefer this simpler version:
    #return hex(int(bin_str, 2))[2:]


def from_hex(hex_str):
    return int(hex_str, 16)


def from_bin(bin_str):
    return from_hex(bin2hex(bin_str))


assert (hex2bin("ABC09") == "1010 1011 1100 0000 1001"), "Logic fail, \"1010 1011 1100 0000 1001\" expected, " + hex2bin("ABC09") + " recieved"
# assert (hex2bin("QWER") == ""), "Logic fail, \"\" expected, " + hex2bin("QWER") + " recieved"
assert (bin2hex("1111 1111 0000") == "FF0"), "Logic fail, \"FF0\" expected, " + bin2hex("1111 1111 0000") + " recieved"
assert (bin2hex("1101 0000") == "D0"), "Logic fail, \"D0\" expected, " + bin2hex("1101 0000") + " recieved"
assert (from_hex("FAC") == 4012), "Logic fail, \"4012\" expected, " + from_hex("FAC")  + " recieved"
assert (from_bin("1101 0111") == 215), "Logic fail, \"215\" expected, " + from_bin("1101 0111") == 215 + " recieved"
def hexToBi(hexStr):
    hexRegex = "[A-Fa-f0-9]+"
    if not bool(re.match(hexRegex, hexStr)):
        return ""
re_hex_format = re.compile(r"[A-Fa-f0-9]+")
def hexToBi(hexStr):
    if not bool(re_hex_format.match(hexStr)):
        return ""
if not re_hex_format.match(hexStr):
    return ""

Context

StackExchange Code Review Q#67688, answer score: 8

Revisions (0)

No revisions yet.