Skip to content

Instantly share code, notes, and snippets.

@not-much-io
Created March 23, 2016 09:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save not-much-io/67dfa5d160acfd8ecdfb to your computer and use it in GitHub Desktop.
Save not-much-io/67dfa5d160acfd8ecdfb to your computer and use it in GitHub Desktop.
from destructuring import destructure
from unittest import TestCase, main
from types import FunctionType
"""
This is just a proposition on how destructuring could be done in Python.
This is just an exercise, the result itself is not meant to be pragmatic for everyday Python code.
Unpacking is a limited form of destructuring:
a, b, c = [1, 2, 3]
The syntax I chose used parameter annotations and a decorator, as these were easily available for use:
@destructure
def foo(d: <some destructuring expression>)
The "perfect" (readability wise) solution and use case would be something like this:
d = {"a": 0, "b": 1, "c": 2}
def foo(a, b, c = d):
return a + b + c
foo(d) => 3
The regular way is:
d = {"a": 0, "b": 1, "c": 2}
def foo(d):
return d["a"] + d["b"] + d["c"]
foo(d) => 3
There are many forms of destructuring, these test cover the basic form:
d = {"a": 0, "b": 1}
def foo(d: ("a", "b")):
return a + b
foo(d) => 1
Maybe you have a better idea how to do this? :)
NOTE:
It is probably "impossible" to do this cleanly as Scoped names are determined at compile time in Python.
For more destructuring syntax example see: https://gist.github.com/john2x/e1dca953548bfdfb9844 (code is in Clojure)
"""
class TestDestructuringBasic(TestCase):
simple_dict = {"key1": 10,
"key2": 1}
@staticmethod
@destructure
def sum_key1_and_key2(d: ('key1', 'key2')):
"""
Destructure the dict d, by getting out these keys and
putting them inside variables with the same name.
"""
return key1 + key2
def test_simple_tuple_expr(self):
val_sum = self.sum_key1_and_key2(self.simple_dict)
self.assertEqual(val_sum, 11)
class TestDestructuringAdvanced(TestCase):
object_dict = {"obj1": 10,
"obj2": "Test String",
"obj3": lambda x: x}
@staticmethod
@destructure
def types_seq(first_type: type, # Type hint
cd: ("obj1", "obj2", "obj3"), # Destructuring
regular_param, # Regular positional argument
*args) -> list: # Variable number arguments + return type (unlikely to be affected)
cd_types = [type(o) for o in (obj1, obj2, obj3)]
return [first_type] + cd_types + [regular_param] + list(args)
def test_tuple_advanced(self):
# Tests if:
# Type hints continue working
# Destructuring works in random position
# Regular positional parameters are unaffected
# Variable number arguments are unaffected
try:
res = self.types_seq(tuple,
self.object_dict,
list,
int, str, str)
except NameError:
raise AssertionError("A NameError was raised, destructured vars"
" were probably not made available to function scope")
self.assertEqual(res, [tuple,
int, str, FunctionType,
list,
int, str, str])
# ToDo test destructuring with dictionary expression
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment