Created
December 4, 2017 07:43
-
-
Save schlarpc/4e15f5fbbc697bab50547a04e6eb59dd to your computer and use it in GitHub Desktop.
Bad ideas in Python: monkey patching decorators on to builtin types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ctypes | |
class PyObject(ctypes.Structure): | |
pass | |
PyObject._fields_ = [ | |
('ob_refcnt', hasattr(ctypes.pythonapi, 'Py_InitModule4_64') and ctypes.c_int64 or ctypes.c_int), | |
('ob_type', ctypes.POINTER(PyObject)), | |
] | |
class SlotsPointer(PyObject): | |
_fields_ = [('dict', ctypes.POINTER(PyObject))] | |
def builtin_setattr(cls, key, value): | |
name = cls.__name__ | |
pointer = SlotsPointer.from_address(id(getattr(cls, '__dict__', name))) | |
namespace = {} | |
ctypes.pythonapi.PyDict_SetItem( | |
ctypes.py_object(namespace), | |
ctypes.py_object(name), | |
pointer.dict, | |
) | |
namespace[name][key] = value | |
def ify(transform): | |
def ify_decorator(func): | |
def ify_wrapper(*args, **kwargs): | |
return transform(func(*args, **kwargs)) | |
return ify_wrapper | |
return ify_decorator | |
for datatype in (list, dict, set, int): | |
builtin_setattr(datatype, 'ify', ify(datatype)) | |
# turn a generator into a list! | |
@list.ify | |
def list_yields(): | |
yield 5 | |
yield 6 | |
yield 7 | |
assert list_yields() == [5, 6, 7] | |
# ... or pairs into a dict! | |
@dict.ify | |
def dict_yields(): | |
yield ('a', 5) | |
yield ('b', 6) | |
yield ('a', 7) | |
assert dict_yields() == {'a': 7, 'b': 6} | |
# ... or dedupe with a set! | |
@set.ify | |
def set_yields(): | |
yield 5 | |
yield 6 | |
yield 5 | |
assert set_yields() == {5, 6} | |
# ... or get your integers going! | |
@int.ify | |
def string_return(): | |
return '88' | |
assert string_return() == 88 | |
# it doesn't have to be a generator! | |
@set.ify | |
def already_list(): | |
return [5, 4, 4] | |
assert already_list() == {5, 4} | |
# and it's ok if it returns nothing! | |
@list.ify | |
def no_yield(): | |
if False: | |
yield 1 | |
assert no_yield() == [] | |
# sigh, i guess you can use it without horrifying monkey patches if you really want | |
@ify(list) | |
def without_monkey(): | |
yield 2 | |
yield 8 | |
assert without_monkey() == [2, 8] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment