Year of the Water Pig

Tonight is the PyWeb Houston meetup and we’re doing ⚡️Talks. I’m doing two. One is related to the code snippet I posted a few days ago. I wanted to look at two different ways to ‘style’ or write your code, depending on what you’re trying to get done.

Background

This is code that takes a JPEG image and converts it into a Base64 encoded ASCII string. This is done to make it easier to attach to a text document. I’ll go into the reasons for why you’d want to do that another time, but this approach was common in the early days of email and it can save space. Plain text is easier to compress. So, here are two ways to write that code.

Style 1: Initial, Verbose

Here we have code you might see after your first pass, when you’re still trying to figure out how to solve the problem. This was MY first pass.

def get_jpeg_screenshot_as_base64_string1(self):
    '''
    Convert JPEG version of screenshot to Base64 string
    :return: 64 bit encoded ascii string
    '''
    # This function returns the path of a saved JPEG screenshot
    jpg_screenshot = get_screenshot_as_jpeg()

    with open(jpg_screenshot, mode="rb") as jpg_image:
        # reading from an image gives a byte string of hexadecimal numbers
        byte_string_hex = jpg_image.read()

    # converts to byte string of Base64 encoded ascii
    byte_string_64 = base64.b64encode(byte_string_hex)

    # converts to standard string of Base64 encoded ascii 
    ascii_string_64 = byte_string_64.decode('ascii')

    # Return the ascii string
    return ascii_string_64

With this approach, each step is layed out clearly.

  • Get the path to the image file
  • Read that image into byte_string_hex
  • Use base64.b64encode which gives you a Base64-encoded byte string
  • Decode that byte string into an ascii string.
  • Return the ascii string to the whatever called the function.

Style 2: Refactored, Concise

This is what you might end up with after you refactor your code. You already have a solution to the problem, now you’re trying to make it ‘cleaner’. It’s much more compact.

  • Get the path to the image file
  • Read that image into byte_string_hex
  • return the converted ascii string
def get_jpeg_screenshot_as_base64_string2(self):
    '''
    Convert JPEG version of screenshot to Base64 string
    :return: 64 bit encoded ascii string
    '''
    # This function returns the path of a saved JPEG screenshot
    jpg_screenshot = get_screenshot_as_jpeg()

    with open(jpg_screenshot, mode="rb") as jpg_image:
        return base64.b64encode(jpg_image.read()).decode('ascii')

When And Where You Might Use Each Style

Style 1

  • When you’re solving a problem for the first time. I think it’s much more important to get something functioning properly first, instead of trying to make it “beautiful” from the start. As you become a more experienced developer, your code will be “more beautiful” from the beginning.
  • When you’re trying to explain something to someone less experienced. For teaching purposes, having each step broken out may be more valuable than having the “slickest” code. It also allows you to inspect each step in the transformation process to see what data types are being converted into what other data types.

Style 2

  • After you’ve solved your problem. If you’re looking for the cleanest, most concise production code, this is probably the way to go.
  • After a refactor. Style 1 still WORKS. But when you go back through to tighten up the code base, that’s the time to look for opportunities to use Style 2.

Update 2017-10-02 21:28

I ended up NOT doing this ⚡️Talk. I did two others instead. I’ll post about one of them later this week.