Re: A proposal (and implementation) to add assignment and LOAD overloading

On Fri, Jun 28, 2019 at 02:44:28AM +1000, Chris Angelico wrote:
I don't understand why it is useless. If the wrapper object is no longer needed, then getself will return the object which is needed, and the wrapper is superfluous and should be garbage collected. If the wrapper object is needed, then getself will return self, and it won't disappear. Are we talking past each other? -- Steven

On Fri, Jun 28, 2019 at 3:55 AM Steven D'Aprano <steve@pearwood.info> wrote:
Without any magic, merely attempting to return the wrapper would cause it to collapse to the underlying object (by calling getself). Therefore, the proposal - to be at all useful - has to have some form of magic to decide whether touching the object gives you the wrapper or calls getself. It's fundamental to the proposal that it be magical. ChrisA

I apologize in advance that this might be slightly off topic from the thread. The idea I want to put forth is probably half baked, but I am wondering if Nate's implementation might be adjusted to incorporate it, and if it might solve a few of the objections. Hopefully someone hasn't already suggested it; I've tried to follow the discussion about this the past couple weeks but there are a lot of messages. The elevator pitch version: add descriptors to modules. Suggested syntax: # inside of my_module a = 1 <-- this does not change # if i specifically want to access a using the of the CURRENT module, i prefix a dot: print(.a) <--- access of a occurs via my_module.__getattr__() when inside my_module # if i specifically want to set a using the (currently not existent) __setattr__() fo the CURRENT module, i do a simliar thing: .a = 2 <-- setting occurs via my_module.__setattr__() when inside my_module In this way, the usage of the module-level attribute getting and setting is entirely VOLUNTARY, and you would expect surprising things to happen when you use the dot. All of the "normal" ways we access variables, which are just names anyway, remain in force: # no changes a = 1 print(a) a = 2 This could be extended to work in other modules: # your_module import my_module my_module.x = 1 <-- magic may occur print(my_module.x) <-- magic may occur Again, this is definitely half baked and I am probably missing something very wrong with it. And I'm sure it has been brought up before. Just throwing it out there as a possible idea related to his discussion. Rick.

On Fri, Jun 28, 2019 at 6:17 AM Ricky Teachey <ricky@teachey.org> wrote:
That's mostly available already! https://www.python.org/dev/peps/pep-0562/ # caller.py import mod print(mod.spam) print(mod.eggs) # mod.py def __getattr__(name): return "<synthetic %r>" % name spam = "ham" rosuav@sikorsky:~/tmp$ python3 caller.py ham <synthetic 'eggs'> I don't think there's any __setattr__ support, though, so this isn't quite everything you're looking for. ChrisA

That's mostly available already!
Indeed, and I've been using it! There is also the "clumsy and obscure" method of adding properties to modules discussed in PEP 549 <https://www.python.org/dev/peps/pep-0549/>: import sys, types class _MyModuleType(types.ModuleType): @property def prop(self, instance, owner): ... sys.modules[__name__].__class__ = _MyModuleType This would allow creating a module with managed attribute access AND setting behaviors. I wonder if this kind of approach might help the folks who want to be able to manage variable getting and setting behavior outside of a class namespace--- you could write a module that makes all of its variables behave just the way you want, and import it like so: import managed_variables_module as m m.x = 1 <-- magic print(m.x)<-- magic I'll bow out of this discussion now, as I don't have much to add. This was just a thought.

On Fri, Jun 28, 2019 at 03:59:14AM +1000, Chris Angelico wrote:
Yeah, definitely talking past each other. Let's get concrete: class Spam: def __getself__(self): print("I can has side-effects!!!") return self x = Spam() x # Calls __getself__ What are you calling "the wrapper" and "the underlying object"? -- Steven

On Fri, Jun 28, 2019 at 6:36 AM Steven D'Aprano <steve@pearwood.info> wrote:
It's a distinction that doesn't exist in your example, because you return self. Consider instead: class Spam: def __getself__(self): print("I can has side-effects!!!") return 42 x = Spam() x # Calls __getself__ The wrapper is the instance of Spam, and the underlying object is the integer 42. Which of these will trigger the print call and work with 42, and which will use the Spam object? print(x) # 1 y = x # 2 x.numerator # 3 def f(q): return 7 # 4.1 f(x) # 4.1 def g(q): return q # 5.1 g(x) # 5.2 Quite frankly, I can't be sure. ChrisA

On Fri, Jun 28, 2019 at 3:55 AM Steven D'Aprano <steve@pearwood.info> wrote:
Without any magic, merely attempting to return the wrapper would cause it to collapse to the underlying object (by calling getself). Therefore, the proposal - to be at all useful - has to have some form of magic to decide whether touching the object gives you the wrapper or calls getself. It's fundamental to the proposal that it be magical. ChrisA

I apologize in advance that this might be slightly off topic from the thread. The idea I want to put forth is probably half baked, but I am wondering if Nate's implementation might be adjusted to incorporate it, and if it might solve a few of the objections. Hopefully someone hasn't already suggested it; I've tried to follow the discussion about this the past couple weeks but there are a lot of messages. The elevator pitch version: add descriptors to modules. Suggested syntax: # inside of my_module a = 1 <-- this does not change # if i specifically want to access a using the of the CURRENT module, i prefix a dot: print(.a) <--- access of a occurs via my_module.__getattr__() when inside my_module # if i specifically want to set a using the (currently not existent) __setattr__() fo the CURRENT module, i do a simliar thing: .a = 2 <-- setting occurs via my_module.__setattr__() when inside my_module In this way, the usage of the module-level attribute getting and setting is entirely VOLUNTARY, and you would expect surprising things to happen when you use the dot. All of the "normal" ways we access variables, which are just names anyway, remain in force: # no changes a = 1 print(a) a = 2 This could be extended to work in other modules: # your_module import my_module my_module.x = 1 <-- magic may occur print(my_module.x) <-- magic may occur Again, this is definitely half baked and I am probably missing something very wrong with it. And I'm sure it has been brought up before. Just throwing it out there as a possible idea related to his discussion. Rick.

On Fri, Jun 28, 2019 at 6:17 AM Ricky Teachey <ricky@teachey.org> wrote:
That's mostly available already! https://www.python.org/dev/peps/pep-0562/ # caller.py import mod print(mod.spam) print(mod.eggs) # mod.py def __getattr__(name): return "<synthetic %r>" % name spam = "ham" rosuav@sikorsky:~/tmp$ python3 caller.py ham <synthetic 'eggs'> I don't think there's any __setattr__ support, though, so this isn't quite everything you're looking for. ChrisA

That's mostly available already!
Indeed, and I've been using it! There is also the "clumsy and obscure" method of adding properties to modules discussed in PEP 549 <https://www.python.org/dev/peps/pep-0549/>: import sys, types class _MyModuleType(types.ModuleType): @property def prop(self, instance, owner): ... sys.modules[__name__].__class__ = _MyModuleType This would allow creating a module with managed attribute access AND setting behaviors. I wonder if this kind of approach might help the folks who want to be able to manage variable getting and setting behavior outside of a class namespace--- you could write a module that makes all of its variables behave just the way you want, and import it like so: import managed_variables_module as m m.x = 1 <-- magic print(m.x)<-- magic I'll bow out of this discussion now, as I don't have much to add. This was just a thought.

On Fri, Jun 28, 2019 at 03:59:14AM +1000, Chris Angelico wrote:
Yeah, definitely talking past each other. Let's get concrete: class Spam: def __getself__(self): print("I can has side-effects!!!") return self x = Spam() x # Calls __getself__ What are you calling "the wrapper" and "the underlying object"? -- Steven

On Fri, Jun 28, 2019 at 6:36 AM Steven D'Aprano <steve@pearwood.info> wrote:
It's a distinction that doesn't exist in your example, because you return self. Consider instead: class Spam: def __getself__(self): print("I can has side-effects!!!") return 42 x = Spam() x # Calls __getself__ The wrapper is the instance of Spam, and the underlying object is the integer 42. Which of these will trigger the print call and work with 42, and which will use the Spam object? print(x) # 1 y = x # 2 x.numerator # 3 def f(q): return 7 # 4.1 f(x) # 4.1 def g(q): return q # 5.1 g(x) # 5.2 Quite frankly, I can't be sure. ChrisA
participants (3)
-
Chris Angelico
-
Ricky Teachey
-
Steven D'Aprano