Hi there,
I love the new type system. I already use it intuitively writing Python. But I think it does not go well with the PEP8 style guide, when it comes to breaking function parameters into multiple lines. Right now PEP8 suggests something like this (please correct me if I'm wrong):
def some_example_func(app_cfg_file: str, tmpl_list_file: str = 'default_list',
style: str = '', input_env: Union[str, None] = None,
tmpl_name: Union[str, None] = None) -> list:
"""This is some example function."""
environments: list = load_yaml(app_cfg_file)['environments']
env: str = input_env or get_environment(environments)
tmpl_cfgs: list = load_templates(tmpl_list_file)
tmpl_name: str = tmpl_name or get_tmpl_cfg(env, tmpl_cfgs)['name']
tmpl_style: str = get_template_style(style)
return [env, tmpl_name, tmpl_style, tmpl_list_file]
I find that this style combined with 'typing' and 'default parameters' makes the parameter definitions of a function difficult to read. This example is not a rare case, 'typing' and 'default parameters' produce a lot of functions with multi-line parameters, since the parameter definitions become much longer.
I fiddled around with this for a while and find that the code becomes quite beautiful, consistent and readable if you apply the following style to every function that has more than one parameter:
def some_example_func(
app_cfg_file: str,
tmpl_list_file: str = 'default_list',
style: str = ''
input_env: Union[str, None] = None,
tmpl_name: Union[str, None] = None
) -> list:
"""This is some example function."""
environments: list = load_yaml(app_cfg_file)['environments']
env: str = input_env or get_environment(environments)
tmpl_cfgs: list = load_templates(tmpl_list_file)
tmpl_name: str = tmpl_name or get_tmpl_cfg(env, tmpl_cfgs)['name']
tmpl_style: str = get_template_style(style)
return [env, tmpl_name, tmpl_style, tmpl_list_file]
I guess the PEP8 section on this was not designed with 'typing' and 'default parameters' in mind. How about an update? :)
Thanks for your feedback!
I agree that for medium-to-long argument lists the style of having one argument per line is better. However I still think this should be a choice up to the programmer (or perhaps a team-specific style rule), not mandated by PEP 8 (else tools like flake8 will end up enforcing it more than is to my liking).
If you want to, you could send a PR to the https://github.com/python/peps repo with this suggestion, as long as you make it clear in the text that it is merely a suggestion and not a requirement.
There are a few other ways to write the example that are still better than the original, I think. Not sure if it's worth trying to pick one particular blessed style.
Double indent for arguments makes them stand out better from the body of the function:
def some_example_func(
app_cfg_file: str,
tmpl_list_file: str = 'default_list',
style: str = '',
input_env: Union[str, None] = None,
tmpl_name: Union[str, None] = None
) -> list:
"""This is some example function."""
environments: list = load_yaml(app_cfg_file)['environments']
env: str = input_env or get_environment(environments)
tmpl_cfgs: list = load_templates(tmpl_list_file)
tmpl_name: str = tmpl_name or get_tmpl_cfg(env, tmpl_cfgs)['name']
tmpl_style: str = get_template_style(style)
return [env, tmpl_name, tmpl_style, tmpl_list_file]
Indent based on ( but one argument per line can look tidy if line length is at least 99 characters or so:
def some_example_func(app_cfg_file: str,
tmpl_list_file: str = 'default_list',
style: str = '',
input_env: Union[str, None] = None,
tmpl_name: Union[str, None] = None) -> list:
"""This is some example function."""
environments: list = load_yaml(app_cfg_file)['environments']
env: str = input_env or get_environment(environments)
tmpl_cfgs: list = load_templates(tmpl_list_file)
tmpl_name: str = tmpl_name or get_tmpl_cfg(env, tmpl_cfgs)['name']
tmpl_style: str = get_template_style(style)
return [env, tmpl_name, tmpl_style, tmpl_list_file]
I also think this should be left up to the the user/team (although PEP 8 can make a _suggestion_).
Thanks for your reply!
If you want to, you could send a PR to the聽https://github.com/python/peps聽repo with this suggestion, as long as you make it clear in the text that it is merely a suggestion and not a requirement.
Ok, cool! I'll send a PR this weekend.
@JukkaL I like your first example. I would use this style for the PR, if you guys are OK with it?
Here is an example module using the style:
https://gist.github.com/feluxe/e4cb86e2ea3ca72a0e821b3dfa07e175
I use the following style and have (unreleased) pylint to check for it:
def some_example_func(
app_cfg_file: str,
tmpl_list_file: str = 'default_list',
style: str = '',
input_env: Union[str, None] = None,
tmpl_name: Union[str, None] = None
) -> list:
"""This is some example function."""
environments: list = load_yaml(app_cfg_file)['environments']
env: str = input_env or get_environment(environments)
tmpl_cfgs: list = load_templates(tmpl_list_file)
tmpl_name: str = tmpl_name or get_tmpl_cfg(env, tmpl_cfgs)['name']
tmpl_style: str = get_template_style(style)
return [env, tmpl_name, tmpl_style, tmpl_list_file]
There are a few other ways to write the example that are still better than the original, I think. Not sure if it's worth trying to pick one particular blessed style.
Double indent for arguments makes them stand out better from the body of the function:
def some_example_func( app_cfg_file: str, tmpl_list_file: str = 'default_list', style: str = '', input_env: Union[str, None] = None, tmpl_name: Union[str, None] = None ) -> list: """This is some example function.""" environments: list = load_yaml(app_cfg_file)['environments'] env: str = input_env or get_environment(environments) tmpl_cfgs: list = load_templates(tmpl_list_file) tmpl_name: str = tmpl_name or get_tmpl_cfg(env, tmpl_cfgs)['name'] tmpl_style: str = get_template_style(style) return [env, tmpl_name, tmpl_style, tmpl_list_file]Indent based on
(but one argument per line can look tidy if line length is at least 99 characters or so:def some_example_func(app_cfg_file: str, tmpl_list_file: str = 'default_list', style: str = '', input_env: Union[str, None] = None, tmpl_name: Union[str, None] = None) -> list: """This is some example function.""" environments: list = load_yaml(app_cfg_file)['environments'] env: str = input_env or get_environment(environments) tmpl_cfgs: list = load_templates(tmpl_list_file) tmpl_name: str = tmpl_name or get_tmpl_cfg(env, tmpl_cfgs)['name'] tmpl_style: str = get_template_style(style) return [env, tmpl_name, tmpl_style, tmpl_list_file]
With this indentation Pycharm says about the closing parenthesis:
PEP8: continuation line with same indent as next logical line
I mean, this version is the most aesthetically pleasing to me, and I would've preferred to stick with it, but it's against PEP8 apparently.
I think this topic is out of scope for this tracker. You could look at what Black does, and complain there if you don't like it.
Wow, Black (https://github.com/python/black) is like Prettier (https://github.com/prettier/prettier), but for Python. Haven't heard of it before. Awesome, now we can use an opinionated code formatter.
Thanks, BDEVIL (https://www.python.org/dev/peps/pep-0401/ or hopefully still BDFL, I haven't been following for a while)!
Most helpful comment
I use the following style and have (unreleased) pylint to check for it: