My code has a mathematical function that iterates over a list of county class objects and returns the county with the max voter turnout along with that turnout in a tuple.
Code here:
class County :
def __init__(self, init_name, init_population, init_voters):
self.name = init_name
self.population = init_population
self.voters = init_voters
def highest_turnout(data) :
max_turnout_county = data[0]
max_turnout = (data[0].voters / data[0].population)
for i in range(0,6):
if (data[i].voters / data[i].population) > max_turnout:
max_turnout_county = data[i].name
max_turnout = (data[i].voters / data[i].population)
return (max_turnout_county, max_turnout)
allegheny = County("allegheny", 1000490, 645469)
philadelphia = County("philadelphia", 1134081, 539069)
montgomery = County("montgomery", 568952, 399591)
lancaster = County("lancaster", 345367, 230278)
delaware = County("delaware", 414031, 284538)
chester = County("chester", 319919, 230823)
bucks = County("bucks", 444149, 319816)
data = [allegheny, philadelphia, montgomery, lancaster, delaware, chester, bucks]
result = highest_turnout(data)
print(result) # prints the output of the function
In its current state, it will return the desired output. ('chester', 0.7215045058280377)
However, if I change the county with the highest output, for example, if I change Allegheny voters from 645469 to 1000480 so that Allegheny is now the max turnout county the output will no longer return the county name in the tuple as expected, but rather the memory location.
Output here: (<submission.County object at 0x7f8e37d3cc18>, 0.9999900048976001)
Why is my code outputting memory location in the second case but not the first case and how would I fix this?
CodePudding user response:
There is bug in your code.
In first line, you are assigning whole County object to the max_county_variable
max_turnout_county = data[0]
later, you are assigning only the attribute name:
max_turnout_county = data[i].name
To fix, you can just change first line to:
max_turnout_county = data[0].name
CodePudding user response:
You initialize max_turnout_county
with an instance of County
from the argument:
max_turnout_county = data[0]
That should be initialized to the name of the county:
max_turnout_county = data[0].name
Alternatively, you can add a turnout property to your County
class:
class County :
def __init__(self, init_name, init_population, init_voters):
self.name = init_name
self.population = init_population
self.voters = init_voters
@property
def turnout(self):
return self.voters / self.population
which simplifies your function greatly:
def highest_turnout(data) :
return max(data, key=lambda c: c.turnout)
# Or, if you really want to return a tuple instead of
# an instance of County,
# c = max(data, key=lambda c: c.turnout)
# return (c.name, c.turnout)
CodePudding user response:
Add this
def __str__(self):
return self.name
str method is used to show name of objects rather than memory location and it must be a string.
CodePudding user response:
What controls how objects of a class will display themseleves when printed is the existense of a __repr__
(or, if you want to distinguish an internal representation for debugging from actually being printed-out, the __str__
) method.
The default __repr__
, howver, if you don't write one in your class, is the class name and memory position.
In your snippet above, when certain conditions are met, max_turnout_county
is assigned with data[i].name
- i.e. the name of your instance, which is a tring. If that condition is not met, the same variable remains assigned with the object itself data[0]
.
Depending on what you want to do with these objetcts, the best thing is to write a proper __repr__
method for them, and do not use only the .name
, but rather, the actual object in any assignments. The object repr can even take care of outputting any other attributes you care about as well, so no need to keep two state variables in your function,and returning a tuple:
class County :
def __init__(self, init_name, init_population, init_voters):
self.name = init_name
self.population = init_population
self.voters = init_voters
# Bonus: yoru object can have a calculated property:
@property
def turnout(self):
return self.voters/self.population
def __repr__(self):
return f"{self.name}, turnout: {(self.turnout * 100):.02f}"
def highest_turnout(data) :
max_turnout_county = data[0]
max_turnout = (data[0].voters / data[0].population)
for i in range(0,6):
if (data[i].turnout) > max_turnout:
max_turnout_county = data[i] # <- this would change
max_turnout = (data[i].voters / data[i].population)
return max_turnout_county
allegheny = County("allegheny", 1000490, 645469)
philadelphia = County("philadelphia", 1134081, 539069)
montgomery = County("montgomery", 568952, 399591)
lancaster = County("lancaster", 345367, 230278)
delaware = County("delaware", 414031, 284538)
chester = County("chester", 319919, 230823)
bucks = County("bucks", 444149, 319816)
data = [allegheny, philadelphia, montgomery, lancaster, delaware, chester, bucks]
result = highest_turnout(data)
print(result) # prints the output of the function
# as part of the bonus - you don't even need a function for that,
# as each object now "knows" its turnout, Python's built-in
# "max" function can find your optimal county:
print(max(data, key=lambda c: c.turnout))