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
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
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
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