I think it is only surprising because it is not something that is familiar. It's not like python python doesn't already have other assignment with different behaviors, for instance:
a = "hello world"
a = 6
print(a) -> 6

class Foo:
    def x(self):
        return self._internal
    def x(self, value):
        self._internal *= value
     def __init__(self, x)
        self._internal = x

a = Foo(4)
a.x = 6
print(a.x) -> 24

This is surprising, but it doesn't mean properties are not useful. Same thing for generic use of descriptors. Most overloaded operators could have surprising behavior:
a = Bar()
b = Bar()
a +=b
type(a) -> str
print(a) -> I eat all your kittens.

It means that if you use code you should understand what it is doing, or at least what side effects it has.

A few interesting things I thought to do with this behavior are:
- True consts, if at say the module level you create instances of a class that define ___setattr__ and __setself__ (or __assign__, I just went for symmetry), and they both passed or raised a value error, then your consts would always be consts.
- Expression templates, avoiding chained operations that may all be loops to differ to one more efficient loop
- More natural syntax for coroutine sendto, or I guess any pipe or proxy like object
- like the above context variables could make use of this
- disk backed variables, on assignment things get synced to disk.
- (copy/assign) like construction, this would be really useful to the pybind11 community. Allow you to take the properties of another instance of something without actually changing what you are pointing to like below:

class Foo:
def __init__(self, a, b,):
self.a = a
self.b = b
def __setself__(self, other):
self.a = other.a
self.b = other.b
class Bar:
def __init__(self, foo):
self.foo = foo
one, two = Foo(5,6), Foo(8,9)
bar, baz = Bar(one), Bar(one)
one = two # copy like assignment
bar.foo.a == 8 && bar.foo.b == 9 && baz.foo.a == 8 && baz.foo.b == 9

Some of these can already be done through method calls on things now, but that doesn't always "read" as well, and ends up looking very java like to me. I am also sure that given the opportunity people more creative than I can come up with all sorts of uses, similar to @classmethod or @static make good use of descriptors.

If this had existed from the start or early on, it would be perfectly natural to read and use, but for now it would be somewhat shocking (in so far as it gets actually used) and I think that is the biggest minus.

On Fri, Jun 21, 2019 at 4:17 PM Ethan Furman <ethan@stoneleaf.us> wrote:
On 06/20/2019 01:25 PM, nate lust wrote:

>  --> class Foo:
>  ...     def __init__(self, o):
>  ...         self.o = o
>  ...     def __setself__(self, v):
>  ...         self.v = v
>  ...

>  --> f = Foo(5)
>  --> print(f)
>  <__main__.Foo object at 0x7f486bb8d300>

>  --> print(f.o)
>  5

>  >>> print(f.v)
>  Traceback (most recent call last):
>     File "<stdin>", line 1, in <module>
>  AttributeError: 'Foo' object has no attribute 'v'

>  --> f = "hello world"
>  --> print(f.v)
>  hello world

>  --> print(f)
>  <__main__.Foo object at 0x7f486bb8d300>

Thank you for doing the work of a proof-of-concept (and to Andrew Barnert for his excellent write-up).  I think this shows exactly why it's a bad idea -- even though I knew what you were doing, having `f` not be a string after the assignment was extremely surprising.


Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-leave@python.org
Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/AFPG55GOZI2U6FK6N2VXYPAQDIJ36ATM/
Code of Conduct: http://python.org/psf/codeofconduct/

Nate Lust, PhD.
Astrophysics Dept.
Princeton University