Python – Can PyCharm help me extract free functions from methods?

Can PyCharm help me extract free functions from methods?… here is a solution to the problem.

Can PyCharm help me extract free functions from methods?

I want to start with

class Sum:
    def __init__(self, a, b):
        self._a = a
        self._b = b

def evaluate(self):
        return self._a + self._b

to

def add(a, b):
    return a + b

class Sum:
    def __init__(self, a, b):
        self._a = a
        self._b = b

def evaluate(self):
        return add(self._a, self._b)

That is, extract the expression self._a + self._b from the method evaluate as a “free function” (not a function of the method). Can PyCharm’s automatic refactoring achieve this?

Solution

I don’t know if this can be done with a single refactoring, but it can be done almost completely automated in 3-4 steps :

  1. Use the Ctrl+Alt+M extraction method
  2. Use F6
  3. to turn it into a free function (“move method to top” in PyCharm terminology)

  4. Use Shift+F6 to rename the parameter
  5. (Optional:) Move the function to the top of the source file (manually).

Detailed steps:

Step 1: Extraction method

  1. Select the expression or statement you want to extract (in this case, self._a + self._b).
  2. Press Ctrl+Alt+M

    or

    From the context menu of your selection, select Refactor> Extract> Method….

  3. In the dialog window that appears, enter a name for your function (add in my example) and click OK.

The result is as follows:

class Sum:
    def __init__(self, a, b):
        self._a = a
        self._b = b

def evaluate(self):
        return self.add()

def add(self):
        return self._a + self._b

Step 2: Turn the method into a free function

Now, let’s take the new method out of the class and convert it into a module-level function:

  1. Select the newly extracted method or place the cursor anywhere in it (signature or body).
  2. Press F6

    or

    Select Refactor> Move…. from the context menu

  3. In the Make Method Top-Level dialog window that appears, leave the “To” input field unchanged (i.e. set to the source file location of the current module) and click Refactor

The result is as follows:

class Sum:
    def __init__(self, a, b):
        self._a = a
        self._b = b

def evaluate(self):
        return add(self._a, self._b)

def add(_a, _b):
    return _a + _b

Step 3: Rename the parameters

As you can see, the refactoring has correctly replaced the self parameter with two parameters to the operand. Unfortunately, it uses the _... name of the corresponding “hidden” instance member.

  1. Select an underscore (_) where the function signature or function uses parameters
  2. Press Shift+F6

    or

    From the context menu of your selection, select Refactor> Rename….

  3. In the Rename dialog window that appears, the underline is already selected, so just press or Del and then press Enter (or hit the Refactor button).

Repeat for the other function parameters.

(In cases where you want to choose a completely different name, just place the cursor in the parameter name you want to change without selecting part of it.) Then the whole name will start selection in the dialog input field and can be overwritten to enter new content. )

The result is as follows:

class Sum:
    def __init__(self, a, b):
        self._a = a
        self._b = b

def evaluate(self):
        return add(self._a, self._b)

def add(a, b):
    return a + b

Step 4 (Optional): Move the function to the top of the source file

(or wherever you want

).

As you can see, PyCharm places the new top-level function after the class. I want to have it before it. Use Shift+Alt+↑ as suggested in John’s answer Moving it in PyCharm 2016.3.2 on GNOME didn’t work for me, so I just selected the function, then cut it with Crtl+x and pasted it where I wanted Crtl+v

Related Problems and Solutions