views:

113

answers:

5

This code seems to smell:

result = None
for item in list:
    if result is None:
        result = item.foo(args)
    else:
        if ClassFred.objects.get(arg1=result) < ClassFred.objects.get(arg1=item.foo(args)):
            result = item.foo(args)

The smelliest part is the utility of 'result'. Would anyone be kind enough sniff it for me?

Thank you! :)

A: 

The logic is too complex. It's difficult to read what you are really doing. Simplify that loop, you are doing too much in there.

This IMHO is already a pretty nasty smell.

Stefano Borini
Isn't this just restating the question? He knows it smells and is trying to improve it.
Roger Pate
+1  A: 

The second-to-last line is so long, I lost patience with it. I'd make those two separate variables with meaningful names, so we can figure out what if x < y is supposed to mean.

How about this:

if not list:
    return None

def get_thing(bar):
    # I don't know what this is, so... here:
    return ClassFred.objects.get(arg1=bar.foo(args))

# way less hassle than that if-else stuff
biggest = get_thing(list[0])
for item in list:
    current = get_thing(item)
    if biggest < current:
            biggest = current
ojrac
Thanks for taking the time to understand it :) I do like your response a lot, so +1, but I believe now that I was looking for the array splicing!
Alex
+3  A: 
L = list # 'list' is a poor variable name, use something else
result = min((n.foo(args) for n in L),
             key=lambda x: ClassFred.objects.get(arg1=x))
# if you don't have to use arg1 as a named parameter:
result = min((n.foo(args) for n in L), key=ClassFred.objects.get)

The min function compares the given items and returns the minimum one (of course :P). What isn't obvious at first is you can control what value is used to compare them, this is the 'key' parameter.

>>> L = [-2, -1, 3]
>>> min(L)
-2
>>> min(L, key=abs)
-1

The key function computes the "comparison key", and that is what is used to compare. The default key function is identity, where the comparison key for an item is the item itself.

>>> def identity(x):
...   return x
>>> min(L, key=identity)
-2

Another example:

>>> min("0000", "11", "222", "3")
"0000" # lexicographical minimum
>>> min("0000", "11", "222", "3", key=len)
"3"

Your code above is using item.foo(args) as values, where item comes from your list; but the result of passing that through ClassFred.objects.get(arg1=..) is used to compare. This means that construct is your key function:

values = (n.foo(args) for n in L) # this is a generator expression
# it is similar to a list comprehension, but doesn't compute or store
# everything immediately

def keyfunc(x):
  return ClassFred.objects.get(arg1=x)

result = min(values, key=keyfunc)

My code at the top just puts this together in one statement.

Roger Pate
I can assure you I don't use list as a variable name :) I just reduced my code to make it easier to read, and there's only one list.arg1 is required as a parameter. In fact, there are actually 2 arguments in my real code, again it was reduced for readability.I couldn't quite get this to work, but +1 because I think the min function would be nice to use here as I am actually trying to find the minimum value.I'll try to get it to work after the weekend :) as I have run out of time trying to get that confusing lambda function to work! Thanks again.
Alex
You are using 'list' as a variable name in your question. If you post your real code, then maybe I can spot how it was misrepresented in the original test case above. Any lambda can be rewritten with a def statement, I'll edit to show that.
Roger Pate
result = min(ClassFred.objects.get(arg1=x) for x in list)
Lars Wirzenius
Lars: that doesn't work the same (different value for result) unless the function returns x, and if it does, there's no sense in calling it.
Roger Pate
Alright Roger, you're officially my hero! :) It now works EXACTLY how I wanted it to! The entire if/else in the nested for loop is GONE, replaced by a single min function. Didn't even know I could do it that neat and succinctly. The explanation helped greatly - it was a bit fiddly, and as it turns out I needed to keep the 'n' object, and run the foo function in the lambda function, using x.foo(args) instead. So the working function is as follows: result = min((n for n in L), key=lambda x: ClassFred.objects.get(arg1=x.foo(args), arg2=x.foo2(args)))
Alex
No need for the generator in that case: `min(L, key=...)`. (My answer does give the same result as the code in your question would, as long as the list isn't empty.)
Roger Pate
+1  A: 

So, I agree it seems like it could be refactored from a readability standpoint, but it should work. This would be my take on it:

# using lst instead of list & at least python 2.5
result = lst[0].foo(args) if lst else None
fxn = ClassFred.objects.get

for item in lst[1:]:
    if fxn(arg1=result) > fxn(arg1=item.foo(args)):
        result = item.foo(args)
BenHayden
Array splicing is what I was looking for, so +1 :) Thank you! Just let me test that complicated looking lambda solution above.
Alex
Hehe... your '<' operator is facing the wrong way! That's okay though I forgive ya :) it works nicely, so thank you. I've run out of time this week to find which answer is the best, so I'll have to answer on Monday. Sorry!
Alex
Oops, i'll go ahead and flip that around. ;)
BenHayden
A: 

This may work, though, I don't think it'll smell better :p

max([ClassFred.objects.get(arg1=item.foo(args)), item.foo(args) for item in list])[1]
Satoru.Logic