15/15 Practice Makes Perfect


#1
def median(nums):

	length = len(nums)
	if length>1:
		print length % 2
		if length % 2 > 0:
			nums.sort()
			median = nums[(length)/2]
		else:
			nums.sort()
			median = (nums[(length/2)-1] + nums[(length/2)]) / 2.0
	else:
		median = nums[0]
	return median

Hello everybody, I ground through and figured out this last problem. I know it looks ugly as heck but can you please give me any tips on how I can write this to be more concise?


#2

Hi @mglmori,

You have the basis of a good solution, but it can be cleaned up a bit.

First, sort a copy of nums instead of sorting nums directly, then work with the copy instead of the original.

Instead of having a variable named median, which is the same as the name of the function, it would be cleaner to use a different name for the variable. You could even dispense with the variable entirely.

You only need these two cases, and you already have the correct calculations of the median for each of them:

  • a list with an odd number of elements
  • a list with an even number of elements

#3

Thank you for your tips. When I used a new list variable and copied the sorted original list, I kept coming across the “NoneType” error. But I will definitely try to use a new list variable instead of working off the original, also, I forgot my function name was the same as my returning variable name, I will try to not avoid that in the future as well.


#4

You probably didn’t make a copy at all, but instead sorted the original and used the return value of that of which there is None. What you would need instead is a way of copying the list and then sorting that, or some different way of sorting which created a copy in the process.

The main reason for making a copy is so that the original remains unchanged, the caller may not want (or expect) the original to be altered (the purpose of your function is to compute median, not to sort, why would it do that?)
The cost of making that copy is low or outweighed by the sorting, in nearly all cases you can consider it free.

I don’t hugely object to using a variable by the same name as the function itself. But the variable isn’t needed at all, may as well return the result directly rather than assigning a variable to it first. Which ever way you do it, including yours, is fine, there are some arguments to be made for all these options.

Rather than sorting in both branches of the if-statement, sort before the if-statement, no need for that duplicated code.

A size-1 list isn’t a special case, no need to test for that. That’s three lines doing absolutely nothing.

Also note that the length of a list isn’t computed every time you call len(list), it’s a stored value that gets updated when the list adds/removes values. Creating your own variable might still be desirable to avoid the clutter of extra parentheses when calling it, or maybe if you want a slightly different name for it for whatever reason. It’s also slightly faster to use local variables, but that is insignificant here (not doing it millions of times)


#5

@ionatan has likely figured out how you encountered that error, and has provided a number of good suggestions regarding refinements. However, if you post your revised code, we can target the specific statements that led to the error.


#6
def median(nums):

	length = len(nums)
	newlist = nums
	newlist.sort()
	
	if length % 2 > 0:
			return newlist[(length)/2]
	else:
			return (newlist[(length/2)-1] + newlist[(length/2)]) / 2.0

OK so I took your’s and @appylpye 's advice and refined my code further. You were right, I removed the test case for when list has only 1 item. Also I’m not using a return variable, instead just returning the calculations. So the trick is to just make the new list equal the old list first and then sort the new list. I was trying to just do those two steps as one with the following syntax:

newlist = nums.sort()

But this obviously doesn’t work because as you said the nums.sort() itself returns a None type value. Thanks for all your help though great inputs to refine my code.


#7

Actually that’s still not a copy. You’ve got two variables referring to the same list.
To make a copy you can slice from the start to the end, or call the type (list) with an iterable (your list) as an argument

newlist = nums        # both refer to the same list, not a copy
newlist = nums[:]     # slice from start to end
newlist = list(nums)  # call list to make a list, use the elements in nums

There’s also a function sorted which creates a list containing the elements provided to it (input might not be a list, anything iterable, but the return value is a list of sorted elements)


#8

See that’s an intricacy of python I didn’t know. I figured newlist = nums was doing the same thing as that as slicing up and then saving as list into the new variable. Thanks @ionatan


#9

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.