patternpythonMinor
Python Barcode Generator
Viewed 0 times
barcodepythongenerator
Problem
I was asked to provide some example code for a job interview and didn't have anything to offer, so wrote the following function.
A barcode generator may seem a bit basic, but there's some logic behind the choice:
The barcode is in the Interleaved 2 of 5 Format. It's a simple format, and there's a Wikipedia article that covers it well.
Update: A new version of this code has been posted here. The new code includes many of the suggestions made below.
``
for black, white in zip(black, white): bits += black + white
# Render: Convert t
A barcode generator may seem a bit basic, but there's some logic behind the choice:
- It's something I recently needed, so not entirely contrived.
- It's simple enough to do in one function, so it can be reviewed easily.
- It's complex enough that a new programmer would make a mess of it.
- It's visual. You can render the result in a browser.
- It's useful, so I could make a little gist out of it.
The barcode is in the Interleaved 2 of 5 Format. It's a simple format, and there's a Wikipedia article that covers it well.
Update: A new version of this code has been posted here. The new code includes many of the suggestions made below.
``
def render(digits):
'''This function converts its input, a string of decimal digits, into a
barcode, using the interleaved 2 of 5 format. The input string must not
contain an odd number of digits. The output is an SVG string.
import barcode
svg = barcode.render('0123456789')
Strings are used to avoid problems with insignificant zeros, and allow
for iterating over the bits in a byte [for zipping].
# Wikipedia, ITF Format: http://en.wikipedia.org/wiki/Interleaved_2_of_5
'''
# Encode: Convert the digits into an interleaved string of bits, with a
# start code of four narrow bars [four zeros].
bits = '0000'
bytes = {
'0': '00110', '5': '10100',
'1': '10001', '6': '01100',
'2': '01001', '7': '00011',
'3': '11000', '8': '10010',
'4': '00101', '9': '01010'
}
for i in range(0, len(digits), 2):
# get the next two digits and convert them to bytes
black, white = bytes[digits[i]], bytes[digits[i+1]]
# zip and concat to bits` 5 pairs of bits from the 2 bytesfor black, white in zip(black, white): bits += black + white
# Render: Convert t
Solution
The code generally looks pretty good, but I think there are some things you could fix up here.
Don't forget the stop code
You have the start code of
Use SVG `
Don't forget the stop code
You have the start code of
0000 but you've forgotten the stop code which is 100. When you calculate, you could just tack it onto the end with bits += '100' Use SVG `
s
Since the purpose is to create an SVG barcode, it make sense to tighten the resulting SVG. You may already know how to use a in SVG and it definitely makes things a little easier to understand here. The way your code is currently structures, it creates four different styles of which are all combinations of narrow/wide and black/white. You could predefine each of those shapes and then simply intantiate them within the body of the svg code. That would make the definition for svg and bar like this:
svg = '''
'''
bar = ''
Using it would then be
svg += bar.format(bit, pos, style)
An improvement would be to create both bars and spaces like this:
Then your loop could look then like this:
for i, bit in enumerate(bits):
width = int(bit) * 2 + 2
svg += bar.format('bs'[i%2], bit, pos)
pos += width
Although that may look verbose, it actually is over 2k shorter for a 12 digit barcode.
Think carefully about data representation
Your code currently translates digits into a series of '1' and '0' characters and then translates again into SVG rectangles. Why not eliminate a step? Your code could just as easily translate them in a single operation.
Use list comprehensions instead of for loops
The use of list comprehensions is almost always faster than a for loop in Python, so we use them when we can. It also tends to make the code shorter. So for example, we could change your loop to calculate the string of bar code bits to calculate all the black bits and then all the white bits like this:
black = "".join([bytes[i] for i in digits[0::2]])
white = "".join([bytes[i] for i in digits[1::2]])
# shuffle them together
databits = "".join("".join(i) for i in zip(black,white))
# create the full bar code string with start and stop
bits = "".join(['0000',databits,'100'])
Doing this with join also saves time. Appending strings with += is very slow in Python.
Prefer xrange to range
When you use range, a whole list object is created, using memory. With xrange, a generator is created instead populated which can save memory. For any reasonably sized bar code this won't make much difference here, but it's good practice for Python 2.7. In Python 3, xrange doesn't exist and range creates a generator, so keep that in mind if you change versions.
Don't draw more than you have to
You're drawing one ` for every bar or space, but it's really not necessary. Instead, you could create one larger rectangle that's the "background" space color and then only draw the black bars.Code Snippets
svg = '''<svg height="50"><defs>
<g id="b0"><rect x="0" y="0" width="2" height="50"/></g>
<g id="b1"><rect x="0" y="0" width="4" height="50"/></g>
</defs>'''
bar = '<use xlink:href="#b{0}" x="{1}" y="0" {2}/>'svg += bar.format(bit, pos, style)<g id="b0"><rect x="0" y="0" width="2" height="50" style="fill:rgb(0,0,0)"/></g>
<g id="b1"><rect x="0" y="0" width="4" height="50" style="fill:rgb(0,0,0)"/></g>
<g id="s0"><rect x="0" y="0" width="2" height="50" style="fill:rgb(255,255,255)"/></g>
<g id="s1"><rect x="0" y="0" width="4" height="50" style="fill:rgb(255,255,255)"/></g>for i, bit in enumerate(bits):
width = int(bit) * 2 + 2
svg += bar.format('bs'[i%2], bit, pos)
pos += widthblack = "".join([bytes[i] for i in digits[0::2]])
white = "".join([bytes[i] for i in digits[1::2]])
# shuffle them together
databits = "".join("".join(i) for i in zip(black,white))
# create the full bar code string with start and stop
bits = "".join(['0000',databits,'100'])Context
StackExchange Code Review Q#49409, answer score: 5
Revisions (0)
No revisions yet.