I am happy to move this discussion to where ever is appropriate. I won't get to it in the next few hours (bed time for my kid) so if you would like feel free to move discussion there, and I guess I can watch this email thread for if you do. Otherwise I will do it when I am free. Nate On Tue, Jun 25, 2019, 6:36 PM Juancarlo Añez <apalala@gmail.com> wrote:
Nate,
I find this mightily interesting! I think it's worth discussing at length.
Is there any chance you'll want to move the discussion to the richer context here? https://discuss.python.org/c/ideas
Regards,
On Tue, Jun 25, 2019 at 5:00 PM nate lust <natelust@linux.com> wrote:
This message is related to two previous threads, but was a sufficiently evolved to warrant a new topic.
I am proposing that two new magic methods be added to python that will control assignment and loading of class instances. This means that if an instance is bound to a variable name, any attempts to rebind that name will result in a call to the __setself__ (name negotiable) of the instance already bound to that name. Likewise when a class instance bound to a name is loaded by the interpreter, if present, the __getself__ method of that instance will be called and its result will be returned instead. I have been internally calling these cloaking variables as they "cloak" the underlying instance, parallelling the idea of shadowing. Feel free to suggest better names.
On first read, that may be surprising, but it extends a behavior pattern that already exists for things like properties (and generically descriptors) to object instances themselves. Similar caveats and behaviors will apply here as well.
A working implementation built against python 3.7 can be found here: https://github.com/natelust/cpython/tree/cloakingVars. This is not pull ready quality code, but the diffs may be interesting to read.
An example for what is possible for this new behavior are instance level properties as seen in the demo at the end of this message.
These changes have minimal impact on the runtime of existing code, and require no modifications to existing syntax other than the use of the names __setself__ and __getself__.
A more detailed write-up with more examples can be found at https://github.com/natelust/CloakingVarWriteup/blob/master/writeup.md, with the example executable demo here: https://github.com/natelust/CloakingVarWriteup/blob/master/examples.py
The demos include: * Variables which keep track of their assignment history, with ability to rollback (possibly useful with try except blocks) * Variables which write out their value to disk when assigned to * An implementation of context variables using only this new framework (does not implement tokens, but could be added) * const variables that can be used to protect module level 'constants' * Instance properties (reproduced below) that allow dynamically adding properties * An implementation of templated expression, to defer the addition of many arrays to a single for loop, saving possibly expensive python iterations.
I am sure the community can come up with many more interesting ideas.
class InstanceProperty:
def __init__(self, wrapped, getter, setter=None): self.wrapped = wrapped self.getter = getter self.setter = setter
def __getself__(self): return self.getter(self.wrapped)
def __setself__(self, value): if self.setter: return self.setter(self.wrapped, value)
class MachineState: def __init__(self): self._fields = {}
def add_input(self, name, start): def getter(slf): return slf._fields[name]
def setter(slf, value): ''' the state of a machine part can only be above zero or below 100 ''' if value < 0: value = 0 if value > 100: value = 100 slf._fields[name] = value setter(self, start) inst_prop = InstanceProperty(self, getter, setter) # noqa: F841 # Need to directly assign the instance property, or decloak it. setattr(self, name, getcloaked('inst_prop'))
machine = MachineState()
for letter, start in zip(['a', 'b', 'c'], [-1, 0, 1]): machine.add_input(letter, start)
print(f"machine.a is {machine.a}") print(f"machine.b is {machine.b}") print(f"machine.c is {machine.c}")
# Assign a value that is too high machine.c = 200
print(f"machine.c is {machine.c}") # Omited from this proposal but present in the linked documentation are # tools for getting the underlying variables, and or rebinding them.
On Fri, Jun 21, 2019 at 9:34 PM nate lust <natelust@linux.com> wrote:
It probably doesn't, this was just something I typed up on the fly, so is unlikely the end result would be what you see above if it was actually implemented.
The only way around that that I can think of now would be if there was two functions, an impl_dictget that actually did the lookup that type could use (and possibly getattr and the like) which would be called in the normal dict get which would just return if the type did not define __getself__ and would call it and return the result if it did.
This is not at all dissimilar to how dict setting works now
On Fri, Jun 21, 2019, 9:27 PM Chris Angelico <rosuav@gmail.com> wrote:
Typing this out though does make me think of an interesting idea. If
On Sat, Jun 22, 2019 at 11:19 AM nate lust <natelust@linux.com> wrote: there was something like __getself__ in addition to __setself__, you could implement things like MyInt. __getself__ would look something like:
class MyInt: def __init__(self, value): self.value = value def __getself__(self): return self.value def __setself__(self, value): raise ValueError("Cant set MyInt") x = MyInt(2) print(x) -> 2 type(x) -> MyInt
Now I have not really thought through how this would work, if it
could work...
How does print know to call getself, but type know not to?
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/3734V6... Code of Conduct: http://python.org/psf/codeofconduct/
-- Nate Lust, PhD. Astrophysics Dept. Princeton University _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/TMU3MJ... Code of Conduct: http://python.org/psf/codeofconduct/
-- Juancarlo *Añez*