In python, variable names are used to refer to objects (such as a list, in your example).
In your first way, you have created a new list object (using some data from the original), and set the variable "magician" to refer to this new object. But then the function ends, and this object isn't used anymore. You could make this method work by returning magicians, and then calling it as:
great_magicians = make_great(names)
In contrast, your second way isn't creating a new list object. Instead, it is going to each individual place in the original list, and replacing each string with a new string. So when you are done, the original list has been modified.