
On Sun, Apr 19, 2020 at 7:58 AM Stephen J. Turnbull < turnbull.stephen.fw@u.tsukuba.ac.jp> wrote:
Alex Hall writes:
And now Lisp bites me, because '::a' means ...
And a single colon also means something else in Lisp.
Yeah, and I already pointed that out myself in this subthread. I don't like these notations, and the conflict with Lisp (which I read a lot of) is part of why. My taste, or the existence of Lisp, doesn't rule the issue, but it's input.
Does it matter much what that notation means in a different language?
Of course it might. That's why we use the usual arithmetic operators for their usual meanings. :: is not a universally used notation, but if we can find an alternative that's even less used, why not use that alternative?
Python will struggle to evolve if it can't conflict with other languages.
Strawman. Nobody said "can't". The question is "better".
OK, that's fair. What about `{foo::}`?
I myself am rarely annoyed by this issue, with
the single exception of "self.foo = foo" in __init__() defs (which can't be handled by these notations).
It can be handled:
``` self.__dict__.update(**, foo, bar, spam) ```
I hope you're kidding.
And here is some actual code of mine using it:
``` setattrs(cls, text=text, program=program, messages=messages, hints=hints) ```
I once wrote code like that as needed; my point was that I now rarely need it. There are already more concise, clearer, less ugly alternatives available, at least for the cases where *I* wrote code like that.
I don't understand what's happening. You clearly said you are still annoyed by `self.foo = foo`, but that this proposal doesn't address that. I pointed out that the proposal does address it. There's one way which is slightly ugly but requires no setup. There's another way that's prettier and just requires introducing one simple function. I would personally be quite happy if I could replace: ``` class A: def __init__(self, foo, bar, spam): self.foo = foo self.spam = spam self.bar = bar ``` with something like: ``` class A: def __init__(self, foo, bar, spam): setattrs(self, **, foo, bar, spam) ``` Wouldn't you? Of course there's also dataclasses, but there's plenty of places where those don't apply so nicely. (the name 'setattrs' may not be the best for the purpose) I can't speak for your needs, only guess. But these
examples of function *calls* don't give me enough information to decide for myself whether I think there's a need to write them, much less show you how I would write the program without them.
I provided a script to help find such cases. Let's make this more concrete. Here is the script again, slightly improved: ``` import ast import linecache import sys from collections import Counter from pathlib import Path root = Path(sys.argv[1]) def main(): counts = Counter() for path in root.rglob("**/*.py"): if 'test' in str(path): continue try: source = path.read_text() tree = ast.parse(source) except (SyntaxError, UnicodeDecodeError): continue for node in ast.walk(tree): if isinstance(node, ast.Call): def is_same_name(keyword: ast.keyword): return ( isinstance(keyword.value, ast.Name) and keyword.value.id == keyword.arg ) args = node.keywords elif isinstance(node, ast.Dict): def is_same_name(pair): key, value = pair return ( isinstance(value, ast.Name) and isinstance(key, (ast.Constant, ast.Str)) and value.id == key.s ) args = zip(node.keys, node.values) else: continue count = sum(map(is_same_name, args)) if count: counts[count] += 1 if count < 7: continue print(f'File "{path}", line {node.lineno}') for lineno in range(node.lineno, node.end_lineno + 1): print(linecache.getline(str(path), lineno), end="") print() print("Counts:", counts) main() ``` I ran this on master of the cpython repo. Here is the output with all the cases with at least 7 same-named values: ``` File "setup.py", line 2232 self.add(Extension('_decimal', include_dirs=include_dirs, libraries=libraries, define_macros=define_macros, undef_macros=undef_macros, extra_compile_args=extra_compile_args, sources=sources, depends=depends)) File "Tools/clinic/clinic.py", line 1083 d = { "docstring_prototype" : docstring_prototype, "docstring_definition" : docstring_definition, "impl_prototype" : impl_prototype, "methoddef_define" : methoddef_define, "parser_prototype" : parser_prototype, "parser_definition" : parser_definition, "impl_definition" : impl_definition, "cpp_if" : cpp_if, "cpp_endif" : cpp_endif, "methoddef_ifndef" : methoddef_ifndef, } File "Lib/shutil.py", line 554 return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks, ignore=ignore, copy_function=copy_function, ignore_dangling_symlinks=ignore_dangling_symlinks, dirs_exist_ok=dirs_exist_ok) File "Lib/tempfile.py", line 642 self._TemporaryFileArgs = {'mode': mode, 'buffering': buffering, 'suffix': suffix, 'prefix': prefix, 'encoding': encoding, 'newline': newline, 'dir': dir, 'errors': errors} File "Lib/compileall.py", line 99 results = executor.map(partial(compile_file, ddir=ddir, force=force, rx=rx, quiet=quiet, legacy=legacy, optimize=optimize, invalidation_mode=invalidation_mode, stripdir=stripdir, prependdir=prependdir, limit_sl_dest=limit_sl_dest), File "Lib/argparse.py", line 879 super().__init__( option_strings=_option_strings, dest=dest, nargs=0, default=default, type=type, choices=choices, required=required, help=help, metavar=metavar) File "Lib/argparse.py", line 917 super(_StoreAction, self).__init__( option_strings=option_strings, dest=dest, nargs=nargs, const=const, default=default, type=type, choices=choices, required=required, help=help, metavar=metavar) File "Lib/argparse.py", line 1009 super(_AppendAction, self).__init__( option_strings=option_strings, dest=dest, nargs=nargs, const=const, default=default, type=type, choices=choices, required=required, help=help, metavar=metavar) File "Lib/argparse.py", line 1038 super(_AppendConstAction, self).__init__( option_strings=option_strings, dest=dest, nargs=0, const=const, default=default, required=required, help=help, metavar=metavar) File "Lib/json/__init__.py", line 234 return cls( skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, indent=indent, separators=separators, default=default, sort_keys=sort_keys, **kw).encode(obj) File "Lib/json/__init__.py", line 173 iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii, check_circular=check_circular, allow_nan=allow_nan, indent=indent, separators=separators, default=default, sort_keys=sort_keys, **kw).iterencode(obj) File "Lib/asyncio/base_events.py", line 1254 opts = dict(local_addr=local_addr, remote_addr=remote_addr, family=family, proto=proto, flags=flags, reuse_address=reuse_address, reuse_port=reuse_port, allow_broadcast=allow_broadcast) File "Lib/logging/__init__.py", line 108 _nameToLevel = { 'CRITICAL': CRITICAL, 'FATAL': FATAL, 'ERROR': ERROR, 'WARN': WARNING, 'WARNING': WARNING, 'INFO': INFO, 'DEBUG': DEBUG, 'NOTSET': NOTSET, } ``` Please make a PR showing how you would refactor some of these.