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.