Elegant Format for a MAC Address in Python 3.2
Question:
I am looking for a more elegant solution to formatting a MAC address with colons. I am using Python 3.2. A fancy list comprehension perhaps?
s=""
h="00233a990c21"
for i in range(0,12,2):
s += h[i:i+2] + ":"
s=s[:-1]
print("s=",s)
Answers:
Not sure how pretty this is, but it will do what you ask:
':'.join([h[i:i+2] for i,j in enumerate(h) if not (i%2)])
gives:
'00:23:3a:99:0c:21'
The easiest solution here is just to use str.join()
>>> ":".join(str_grouper(2, "00233a990c21"))
'00:23:3a:99:0c:21'
Here using a modified version of the grouper()
recipe from the itertools
docs:
def str_grouper(n, iterable):
args = [iter(iterable)] * n
for part in zip(*args): #itertools.izip in 2.x for efficiency.
yield "".join(part)
Your code is easily converted to a comprehension form:
':'.join(h[i:i+2] for i in range(0,12,2))
Well, I might start with something pretty specific, since you know that it is an MAC address you know the exact size and format.
print "%s:%s:%s:%s:%s:%s" % (h[0:2], h[2:4], h[4:6], h[6:8], h[8:10], h[10:12])
But we can make this better if we create a class and then tell it how to format it’s string.
class Mac():
def __init__(self, mac):
self.mac = mac
def __str__(self):
return "%s:%s:%s:%s:%s:%s" % (
self.mac[0:2],
self.mac[2:4],
self.mac[4:6],
self.mac[6:8],
self.mac[8:10],
self.mac[10:12])
m = Mac("123456789012")
print m
>>> import itertools
>>> h = '00233a990c21'
>>> ':'.join(a+b for a, b in (itertools.izip(
... itertools.compress(h, itertools.cycle((1,0))),
... itertools.compress(h, itertools.cycle((0,1))))))
>>> '00:23:3a:99:0c:21'
Does that win for the highest density of parentheses?
This is not the shortest solution, but it accepts all common types of mac formats as inputs. It also does some validation checks.
import re
def format_mac(mac: str) -> str:
mac = re.sub('[.:-]', '', mac).lower() # remove delimiters and convert to lower case
mac = ''.join(mac.split()) # remove whitespaces
assert len(mac) == 12 # length should be now exactly 12 (eg. 008041aefd7e)
assert mac.isalnum() # should only contain letters and numbers
# convert mac in canonical form (eg. 00:80:41:ae:fd:7e)
mac = ":".join(["%s" % (mac[i:i+2]) for i in range(0, 12, 2)])
return mac
Here is a list of mac address strings and whether they will be considered as valid or invalid:
'008041aefd7e', # valid
'00:80:41:ae:fd:7e', # valid
'00:80:41:AE:FD:7E', # valid
'00:80:41:aE:Fd:7E', # valid
'00-80-41-ae-fd-7e', # valid
'0080.41ae.fd7e', # valid
'00 : 80 : 41 : ae : fd : 7e', # valid
' 00:80:41:ae:fd:7e ', # valid
'00:80:41:ae:fd:7ent', # valid
'aa:00:80:41:ae:fd:7e', # invalid
'0:80:41:ae:fd:7e', # invalid
'ae:fd:7e', # invalid
'$$:80:41:ae:fd:7e', # invalid
All valid ones will be returned in the canonical form as:
'00:80:41:ae:fd:7e'
from netaddr import EUI, mac_unix_expanded
print(EUI('00:01:02:03:04:05', dialect=mac_unix_expanded))
print(EUI('1-2-3-4-5-6', dialect=mac_unix_expanded))
Not new but still most elegant I think:
import re
':'.join(re.findall('..', '08002714f616'))
late answer, but for those who, like I do, retrieve the MAC address from the uuid
module, you could use this since the output you get is an integer:
from uuid import getnode
# convert the integer to an uppercase string
addr = hex(int(getnode())).split('x')[1].upper()
# format it
addr = ':'.join([addr[x:x+2].upper() for x in range(0, len(addr), 2)])
This code also works for an odd number of characters once in hex
form e.g.:
Input: 6969696969
Output: 19:F6:D2:2C:9
I guess leading zeros are not handled, but let’s pretend that this won’t happen.
I am looking for a more elegant solution to formatting a MAC address with colons. I am using Python 3.2. A fancy list comprehension perhaps?
s=""
h="00233a990c21"
for i in range(0,12,2):
s += h[i:i+2] + ":"
s=s[:-1]
print("s=",s)
Not sure how pretty this is, but it will do what you ask:
':'.join([h[i:i+2] for i,j in enumerate(h) if not (i%2)])
gives:
'00:23:3a:99:0c:21'
The easiest solution here is just to use str.join()
>>> ":".join(str_grouper(2, "00233a990c21"))
'00:23:3a:99:0c:21'
Here using a modified version of the grouper()
recipe from the itertools
docs:
def str_grouper(n, iterable):
args = [iter(iterable)] * n
for part in zip(*args): #itertools.izip in 2.x for efficiency.
yield "".join(part)
Your code is easily converted to a comprehension form:
':'.join(h[i:i+2] for i in range(0,12,2))
Well, I might start with something pretty specific, since you know that it is an MAC address you know the exact size and format.
print "%s:%s:%s:%s:%s:%s" % (h[0:2], h[2:4], h[4:6], h[6:8], h[8:10], h[10:12])
But we can make this better if we create a class and then tell it how to format it’s string.
class Mac():
def __init__(self, mac):
self.mac = mac
def __str__(self):
return "%s:%s:%s:%s:%s:%s" % (
self.mac[0:2],
self.mac[2:4],
self.mac[4:6],
self.mac[6:8],
self.mac[8:10],
self.mac[10:12])
m = Mac("123456789012")
print m
>>> import itertools
>>> h = '00233a990c21'
>>> ':'.join(a+b for a, b in (itertools.izip(
... itertools.compress(h, itertools.cycle((1,0))),
... itertools.compress(h, itertools.cycle((0,1))))))
>>> '00:23:3a:99:0c:21'
Does that win for the highest density of parentheses?
This is not the shortest solution, but it accepts all common types of mac formats as inputs. It also does some validation checks.
import re
def format_mac(mac: str) -> str:
mac = re.sub('[.:-]', '', mac).lower() # remove delimiters and convert to lower case
mac = ''.join(mac.split()) # remove whitespaces
assert len(mac) == 12 # length should be now exactly 12 (eg. 008041aefd7e)
assert mac.isalnum() # should only contain letters and numbers
# convert mac in canonical form (eg. 00:80:41:ae:fd:7e)
mac = ":".join(["%s" % (mac[i:i+2]) for i in range(0, 12, 2)])
return mac
Here is a list of mac address strings and whether they will be considered as valid or invalid:
'008041aefd7e', # valid
'00:80:41:ae:fd:7e', # valid
'00:80:41:AE:FD:7E', # valid
'00:80:41:aE:Fd:7E', # valid
'00-80-41-ae-fd-7e', # valid
'0080.41ae.fd7e', # valid
'00 : 80 : 41 : ae : fd : 7e', # valid
' 00:80:41:ae:fd:7e ', # valid
'00:80:41:ae:fd:7ent', # valid
'aa:00:80:41:ae:fd:7e', # invalid
'0:80:41:ae:fd:7e', # invalid
'ae:fd:7e', # invalid
'$$:80:41:ae:fd:7e', # invalid
All valid ones will be returned in the canonical form as:
'00:80:41:ae:fd:7e'
from netaddr import EUI, mac_unix_expanded
print(EUI('00:01:02:03:04:05', dialect=mac_unix_expanded))
print(EUI('1-2-3-4-5-6', dialect=mac_unix_expanded))
Not new but still most elegant I think:
import re
':'.join(re.findall('..', '08002714f616'))
late answer, but for those who, like I do, retrieve the MAC address from the uuid
module, you could use this since the output you get is an integer:
from uuid import getnode
# convert the integer to an uppercase string
addr = hex(int(getnode())).split('x')[1].upper()
# format it
addr = ':'.join([addr[x:x+2].upper() for x in range(0, len(addr), 2)])
This code also works for an odd number of characters once in hex
form e.g.:
Input: 6969696969
Output: 19:F6:D2:2C:9
I guess leading zeros are not handled, but let’s pretend that this won’t happen.