The Pythonic solution is to use a dictionary.
def fldAsType(fld):
fldLookup = {
"Neighborhood" : 1,
"Block" : 1,
"Locality" : 1,
"Apartment" : 2,
"Unit" : 2,
"Condo" : 3,
"Penthouse" : 3,
}
try:
return fldLookup[fld]
except:
return -999
Note that "Type" is a dangerous name, because of the built-in type
operator.
Conforming to the PEP 8 style guide will make your code integrate more easily with code found on the web. The relevant piece is that classes start with upper-case and methods with lower-case.
So I just got a chance to benchmark this in Python 2.7, and found that the "Pythonic" "EAFP" principle ("It's easier to ask for forgiveness than permission") didn't actually apply -- structuring this function as
def fldAsType(fld):
fldLookup = {
"Neighborhood" : 1,
"Block" : 1,
"Locality" : 1,
"Apartment" : 2,
"Unit" : 2,
"Condo" : 3,
"Penthouse" : 3,
}
return fldLookup[fld] if fld in fldLookup else -999
was three times faster than using the exception handling, but was still four times slower than the if
/elif
/else
cascade. Which seemed wrong. So I moved the lookup initialization outside the function definition:
fldLookup = {
"Neighborhood" : 1,
"Block" : 1,
"Locality" : 1,
"Apartment" : 2,
"Unit" : 2,
"Condo" : 3,
"Penthouse" : 3,
}
def fldAsType(fld):
return fldLookup[fld] if fld in fldLookup else -999
And then the dictionary was 10% faster than if
/elif
/else
.
Then I applied EAFP:
fldLookup = {
"Neighborhood" : 1,
"Block" : 1,
"Locality" : 1,
"Apartment" : 2,
"Unit" : 2,
"Condo" : 3,
"Penthouse" : 3,
}
def fldAsType(fld):
try:
return fldLookup[fld]
except:
return -999
and the dictionary was now 35% faster without any failed lookups, but 16% slower if there were 2% None values in the lookup population.
So, the lessons here are:
- Never place dictionary initialization inside a method
- Only use EAFP when you're reasonably certain you won't need forgiveness
Benchmark script:
import random
import datetime
def fldAsType0(fld):
if fld in ["Neighborhood", "Block", "Locality"]:
value = 1
elif fld in ["Apartment", "Unit"]:
value = 2
elif fld in ["Condo", "Penthouse"]:
value = 3
else:
value = 999 # or whatever you want, for example None
return value
def fldAsType1(fld):
fldLookup = {
"Neighborhood" : 1,
"Block" : 1,
"Locality" : 1,
"Apartment" : 2,
"Unit" : 2,
"Condo" : 3,
"Penthouse" : 3,
}
try:
return fldLookup1[fld]
except:
return -999
def fldAsType2(fld):
fldLookup2 = {
"Neighborhood" : 1,
"Block" : 1,
"Locality" : 1,
"Apartment" : 2,
"Unit" : 2,
"Condo" : 3,
"Penthouse" : 3,
}
return fldLookup2[fld] if fld in fldLookup2 else -999
fldLookup3 = {
"Neighborhood" : 1,
"Block" : 1,
"Locality" : 1,
"Apartment" : 2,
"Unit" : 2,
"Condo" : 3,
"Penthouse" : 3,
}
def fldAsType3(fld):
return fldLookup3[fld] if fld in fldLookup3 else -999
def fldAsType4(fld):
try:
return fldLookup3[fld]
except:
return -999
# choices not in Python 2.7
def choices(population, weights=None, cum_weights=None, k=1):
result = []
newpop = []
for p in population:
for w in weights:
newpop.append(p)
for i in range(k):
result.append(random.choice(newpop))
return result
# Generate 100000 values (even distribution)
random.seed(12345)
sampleSize = 1000000
uniformDistro = []
sortedDistro = []
options1 = ["Neighborhood", "Block", "Locality", "Apartment", "Unit", "Condo", "Penthouse"]
options2 = sorted(options1)
options3 = random.sample(options1, k=len(options1))
options3.append(None)
options4 = random.sample(options1, k=len(options1))
for i in range(sampleSize):
uniformDistro.append(random.choice(options1))
sortedDistro.append(random.choice(options2))
missingDistro = choices(options3,[7,7,7,7,7,7,7,1],k=sampleSize)
weightedDistro = choices(options4,[21,13,8,5,3,2,1],k=sampleSize)
start = datetime.datetime.utcnow()
for v in uniformDistro:
t = fldAsType0(v)
print("{:d} : {:12s} : {:7.3f}".format(0,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in sortedDistro:
t = fldAsType0(v)
print("{:d} : {:12s} : {:7.3f}".format(0,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in missingDistro:
t = fldAsType0(v)
print("{:d} : {:12s} : {:7.3f}".format(0,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in weightedDistro:
t = fldAsType0(v)
print("{:d} : {:12s} : {:7.3f}".format(0,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in uniformDistro:
t = fldAsType1(v)
print("{:d} : {:12s} : {:7.3f}".format(1,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in sortedDistro:
t = fldAsType1(v)
print("{:d} : {:12s} : {:7.3f}".format(1,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in missingDistro:
t = fldAsType1(v)
print("{:d} : {:12s} : {:7.3f}".format(1,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in weightedDistro:
t = fldAsType1(v)
print("{:d} : {:12s} : {:7.3f}".format(1,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in uniformDistro:
t = fldAsType2(v)
print("{:d} : {:12s} : {:7.3f}".format(2,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in sortedDistro:
t = fldAsType2(v)
print("{:d} : {:12s} : {:7.3f}".format(2,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in missingDistro:
t = fldAsType2(v)
print("{:d} : {:12s} : {:7.3f}".format(2,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in weightedDistro:
t = fldAsType2(v)
print("{:d} : {:12s} : {:7.3f}".format(2,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in uniformDistro:
t = fldAsType3(v)
print("{:d} : {:12s} : {:7.3f}".format(3,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in sortedDistro:
t = fldAsType3(v)
print("{:d} : {:12s} : {:7.3f}".format(3,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in missingDistro:
t = fldAsType3(v)
print("{:d} : {:12s} : {:7.3f}".format(3,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in weightedDistro:
t = fldAsType3(v)
print("{:d} : {:12s} : {:7.3f}".format(3,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in uniformDistro:
t = fldAsType4(v)
print("{:d} : {:12s} : {:7.3f}".format(4,"Uniform",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in sortedDistro:
t = fldAsType4(v)
print("{:d} : {:12s} : {:7.3f}".format(4,"Sorted",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in missingDistro:
t = fldAsType4(v)
print("{:d} : {:12s} : {:7.3f}".format(4,"Missing",(datetime.datetime.utcnow() - start).total_seconds()))
start = datetime.datetime.utcnow()
for v in weightedDistro:
t = fldAsType4(v)
print("{:d} : {:12s} : {:7.3f}".format(4,"Fibonacci",(datetime.datetime.utcnow() - start).total_seconds()))
#EOF