On a website, I am trying to sort a list of shop by their relative position of the user. Let me explain.
A shop looks something like this:
class Shop(models.Model):
latitude = models.DecimalField(max_digits=9, decimal_places=6)
longitude = models.DecimalField(max_digits=9, decimal_places=6)
I get the position of the user in the session.
request.session['user_latitude']
request.session['user_longitude']
So now I got a list of shop and I want to sort them. So I tried this:
def distance_of_the_shop(shop):
# compute the distance between the shop and the user and return it
return computed_distance
sorted(shop_list, key=distance_of_the_shop)
The question is pretty simple, how to pass more than one argument to the method distance_of_the_shop
?
Just wrap the call in a lambda:
ulong, ulat = request.session['user_latitude'], request.session['user_longitude']
sorted(shop_list, key=lambda shop: distance_of_the_shop(shop, ulong, ulat))
and add two more arguments to the distance_of_the_shop()
function to receive the longitude and latitude.
The sorted()
function calls the key
for each value in shop_list
, but nothing says the callable cannot itself call other functions. A lambda
is the easiest way to create a new wrapper function that does just that.
You could also use a functools.partial()
object , provided the longitude and latitude values can be passed in as keyword arguments, or accepts those two values as the first two positional arguments. Treating them as keyword arguments is probably best, even if they are given a position (no default value), you can use their names as keyword arguments in the partial()
.
Assuming the definition is:
def distance_of_the_shop(shop, long, lat):
# ...
then use
sorted(shop_list, key=partial(distance_of_the_shop, long=ulong, lat=ulat))
and sorted()
will pass each shop
to the partial()
, which in turn calls distance_of_the_shop(shop, long=ulong, lat=ulat)
Your question is hard to comprehend because you never define a distance
function, and the function you do provide, distance_of_the_shop
, actually takes a single argument.
If I understand correctly, you would like distance_of_the_shop
to receive the current user and the shop being compared. To achieve that, use a lambda
:
shop_list.sort(key=lambda shop: distance_of_the_shop(user, shop))
Also note that it doesn't make sense to call sorted
without assigning its value to some variable or container. If you want to sort a list in-place, use its sort
method, as shown above.
First, we need a distance function (this one calculates great-circle distances):
from math import radians, cos, sin, asin, sqrt
def haversine(lon1, lat1, lon2, lat2):
"""
Calculate the great circle distance between two points
on the earth (specified in decimal degrees)
"""
# from stackoverflow.com/questions/4913349/haversine-formula-in-python-bearing-and-distance-between-two-gps-points#4913653
# convert decimal degrees to radians
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
# haversine formula
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * asin(sqrt(a))
# 6367 km is the radius of the Earth
km = 6367 * c
return km
then we define a distance-function per user:
def dist_from_user(user):
# This is kind of funky, because the `user` parameter is
# not used, we simply assume that user is the current session-holder.
# It would make more sense if we actually had more than
# one user to choose between.
lat = float(request.session['user_latitude'])
lon = float(request.session['user_longitude'])
def shop_dist(shop):
s_lat = float(shop.latitude)
s_lon = float(shop.longitude)
return haversine(lon, lat, s_lon, s_lat)
return shop_dist
and call it like
shop_list.sort(key=dist_from_user(user))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.