Dear all,
Despite the general beauty of Python, I find myself constantly violating
the "don't repeat yourself" maxim when trying to write clear, fully
documented code. Take the following example:
def func_1(a: int = 1, b: float = 2.5) -> float:
"""
Something about func_1
:param a: Something about param a
:param b: Something else about param b
:return: Something about return value of func_1
"""
return a*b
def func_2(c:float=3.4, d: bool =True) -> float:
"""
Something about func_2
:param c: Something about param c
:param d: Something else about param d
:return: Something about return value
"""
return c if d else -c
def main_function(a: int = 1, b: float = 2.5, d: bool = True) -> float:
"""
Something about main_function
:param a: Something about param a
:param b: Something else about param b
:param d: Something else about param d
:return: Something about return value
"""
return func_2(func_1(a=a, b=b), d=d)
Which has the following problems:
- Defaults are defined in multiple places, which very easily leads to bugs
(I'm aware of **kwargs but it obfuscates function interfaces and usually
does more harm than good)
- Types are defined in multiple places
- Documentation is copy-pasted when referencing a single thing from
different places. (I can't count the number of types I've written ":param
img: A (size_y, size_x, 3) RGB image" - I could now just reference a single
RGB_IMAGE_DOC variable)
- Argument names need to be written twice - in the header and documentation
- and it's up to the user / IDE to make sure they stay in sync.
I propose to resolve this with the following changes:
- Argument/return documentation can be made inline with a new "?"
operator. Documentation becomes a first class citizen.
- Argument (type/default/doc) can be referenced by "func.args.<arg_name>.type"
/ "func.args.<arg_name>.default" / "func.args.<arg_name>.doc". Positional
reference: e.g. "func.args[1].default" also allowed. If not specified,
they take a special, built-in "Undefined" value (because None may have
another meaning for defaults). Return type/doc can be referenced with
"func.return.type"
/ "func.return.doc".
This would result in the following syntax:
def func_1(
a: int = 1 ? 'Something about param a',
b: float = 2.5 ? 'Something else about param b',
) -> float ? 'Something about return value of func_1':
""" Something about func_1 """
return a*b
def func_2(
c: float=3.4 ? 'Something about param c',
d: bool =True ? 'Something else about param d',
) -> float ? 'Something about return value':
""" Something about func_2 """
return c if d else -c
def main_function(
a: func_1.args.a.type = func_1.args.a.default ?
func_1.args.a.doc,
b: func_1.args.b.type = func_1.args.b.default ?
func_1.args.b.doc,
d: func_2.args.d.type = func_2.args.d.default ?
func_2.args.d.doc,
) -> func_2.return.type ? func2.return.doc:
""" Something about main_function """
return func_2(func_1(a=a, b=b), d=d)
If the main_function header seems repetitious (it does) we could allow for
an optional shorthand notation like:
def main_function(
a :=? func_1.args.a,
b :=? func_1.args.b,
d :=? func_2.args.d,
) ->? func_2.return:
""" Something about main_function """
return func_2(func_1(a=a, b=b), d=d)
Where "a :=? func_1.args.a" means "argument 'a' takes the same
type/default/documentation as argument 'a' of func_1".
So what do you say? Yes it's a bold move, but I think in the long term
it's badly needed. Perhaps something similar has been proposed already
that I'm not aware of.
_______________________________________________
Python-ideas mailing list
[email protected]
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/