3

I'm using PyCharm. I start defining a class:

class A:
    def __init__(self,a,b,c):

I want it to look like this:

class A:
    def __init__(self,a,b,c):
        self.a = a
        self.b = b
        self.c = c

Using alt-enter I can get PyCharm to add a single field to the class from __init__ but then I have to go back and do it again individually for each variable, which eventually gets to be annoying. Is there any shortcut that does them all at once?

bad_coder
  • 11,289
  • 20
  • 44
  • 72
mrip
  • 14,913
  • 4
  • 40
  • 58
  • It's a nice question but I don't think there is option for that. Mainly, because whenever you add a new parameter to `__init__`, PyCharm offers you assigning that parameter as attribute. But it also offers you to name attribute different from parameter. I think assigning one by one is intentional... – Dinko Pehar Nov 03 '19 at 16:45

1 Answers1

2

PyCharm doesn't have a built-in intention action to automatically declare and set all the instance variables from the constructor signature.

So the 2 main alternatives to implement this functionality would be:

  1. Using an External tool.
  2. Using a Live Template variables and functions.
  3. (I suppose a Plugin could be developed, but would likely involve more work.)

Using an external tool has its own set of problems, see the answer to PyCharm: Run black -S on region for a general idea of what it involves.

For the problem in this question a Live Template is probably the easiest to integrate. The main drawback is having to use Groovy Script (the list of Live Template functions offers the possibility of using RegEx - I think the former is a better approach.)

So using the following Groovy Script test.groovy

import java.util.regex.Pattern
import java.util.regex.Matcher

// the_func = "    fun_name(a, b, c)"  // For stand-alone testing.
the_func = _1  // PyCharm clipboard() function argument.

int count_indent = the_func.indexOf(the_func.trim());  // Count indentation whitspaces.

def arg_list_str

def pattern = "\\((.*?)\\)"
Matcher m = Pattern.compile(pattern).matcher(the_func);

while (m.find()) {
    arg_list_str = m.group(1)  // Retrieve parameters from inside parenthesis.
}

// println arg_list_str
String[] arguments = arg_list_str.split(",")  // Splits on parameter separator.

def result = ['\n']  // Ends the constructor line for convenience.

for(arg in arguments) {
    arg = arg.trim()  // Remove whitspaces surrounding parameter.
    if(arg == "self")  // Skips first method parameter 'self'.
        continue
    arg = " ".repeat(count_indent+4) + "self." + arg + " = " + arg + "\n" 
    result.add(arg)
}

String joinedValues = result.join("")

return joinedValues

And setting the Live Template at File > Settings > Editor > Live Templates as shown in the screenshot. Using the clipboard() function together with groovyScript("c:\\dir_to_groovy\\test.groovy", clipboard()); from the Predefined functions. (Although the Groovy Script can be written on a single-line it's easier in this case to put it in an external file).

enter image description here

Selecting the line including indentation, copying to the clipboard (Ctrl + C), and choosing the Live Template pressing Ctrl + J

enter image description here

The declaration of the instance variables is generated (after pressing enter the indentation is adjusted.)

enter image description here

End notes.

The signature parser in the included Groovy Script is simplistic and only concerned with solving the problem in the question. With some research a complete signature parser is probably available that would handle complex type hints and default arguments. But for purpose of the example it works adequately.

bad_coder
  • 11,289
  • 20
  • 44
  • 72
  • Has anyone created the complete signature parser that would handle complex type hints and default arguments? – Slazer Sep 13 '21 at 08:51
  • 1
    @Slazer I haven't found one. [mypy](https://github.com/python/mypy) probably has such a parser. The problem is type hints have been changing drastically with every Python version, currently not even PyCharm linter analyzes type hints 100% right. So maintaining such a continuously changing parser is no trivial task. I wrote this answer as a proof-of-concept, if you find the parser integrating it would go along the steps in this answer. – bad_coder Sep 13 '21 at 12:29