Skip to content

Instantly share code, notes, and snippets.

@CircuitSacul
Last active August 11, 2023 01:51
Show Gist options
  • Save CircuitSacul/669744ee86cb13fa0aa9380cf034e17c to your computer and use it in GitHub Desktop.
Save CircuitSacul/669744ee86cb13fa0aa9380cf034e17c to your computer and use it in GitHub Desktop.
Strings weren't mutable, they said.
import typing as t
def ref(obj):
class Ref:
pass
slf = Ref()
slf.__obj = obj
build_methods(slf)
return slf
def build_methods(slf):
obj = slf.__obj
Ref = type(slf)
def make_ref_meth(orig_meth):
def ref_meth(*args, **kwargs):
if args and type(args[0]) is Ref:
return orig_meth(slf.__obj, *args[1:], **kwargs)
else:
return orig_meth(*args, **kwargs)
return ref_meth
def make_inplace_meth(ref_meth):
def inplace_meth(slf, *args, **kwargs):
oldtyp = type(slf.__obj)
slf.__obj = ref_meth(slf, *args, **kwargs)
if oldtyp is not type(slf.__obj):
build_methods(slf)
return slf
return inplace_meth
for k, v in vars(type(obj)).items():
if k in ["__getattribute__"]:
continue
if isinstance(v, t.Callable):
setattr(Ref, k, ref_meth := make_ref_meth(v))
if k.startswith("__") and k.endswith("__"):
name = k[2:-2]
if name.startswith("i"):
continue
inplace_name = f"__i{name}__"
if hasattr(type(obj), inplace_name):
continue
setattr(Ref, inplace_name, make_inplace_meth(ref_meth))
else:
setattr(Ref, k, v)
return slf
string = ref("hello")
num = ref(1)
def double(val):
val += val
double(num)
double(string)
print(num) # 2
print(string) # hellohello
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment