Skip to content

Instantly share code, notes, and snippets.

@fish2000
Forked from wolfv/multiple_dispatch.py
Last active November 20, 2018 20:25
Show Gist options
  • Save fish2000/1af4b852d20b7568a9b9c90fe2346b6d to your computer and use it in GitHub Desktop.
Save fish2000/1af4b852d20b7568a9b9c90fe2346b6d to your computer and use it in GitHub Desktop.

Revisions

  1. fish2000 revised this gist Nov 20, 2018. 1 changed file with 36 additions and 5 deletions.
    41 changes: 36 additions & 5 deletions multiple_dispatch.py
    Original file line number Diff line number Diff line change
    @@ -13,6 +13,7 @@

    import typing as tx
    import re
    import types
    import collections.abc

    __all__ = ('overloaded', 'T', 'U')
    @@ -32,6 +33,12 @@ def origin(arg):
    def is_generic_sequence(arg):
    return origin(arg) is collections.abc.Sequence

    def is_generic_iterable(arg):
    return origin(arg) is collections.abc.Iterable

    def can_be_iterated(arg):
    return is_generic_sequence(arg) or is_generic_iterable(arg)

    def to_matchgroup(arg, groups):
    if type(arg) is tx.TypeVar:
    if arg in groups:
    @@ -43,10 +50,12 @@ def to_matchgroup(arg, groups):
    return to_regex(arg, groups)

    def to_regex(typevar, groups):
    if typevar in { float, int, str }:
    if typevar in { float, int, str, bytes }:
    return nameof(typevar)
    elif is_generic_sequence(typevar):
    return "(?:list|set|tuple)\[{}\]".format(to_matchgroup(typevar.__args__[0], groups))
    return "(?:list|tuple)\[{}\]".format(to_matchgroup(typevar.__args__[0], groups))
    elif is_generic_iterable(typevar):
    return "(?:set|frozenset|generator)\[{}\]".format(to_matchgroup(typevar.__args__[0], groups))
    return ".*?"

    def get_element_types(sequence):
    @@ -59,15 +68,17 @@ def get_element_types(sequence):
    return out

    def to_callee(arg):
    if type(arg) in { float, int }:
    if type(arg) in { float, int, str, bytes }:
    return typename(arg)
    elif type(arg) in { list, set, tuple }:
    elif type(arg) in { list, tuple, set, frozenset }:
    t = typename(arg) + '[{}]'
    eltypes = get_element_types(arg)
    if len(eltypes) == 1:
    return t.format(nameof(eltypes[0]))
    else:
    raise RuntimeError("Not implemented yet.")
    elif type(arg) in { types.GeneratorType }:
    return typename(arg) + '[.*?]'
    else:
    raise RuntimeError("Not implemented yet.")

    @@ -121,9 +132,29 @@ def add(a: tx.Sequence[T], b: tx.Sequence[str]):
    def add(a: tx.Sequence[T], b: tx.Sequence[U]):
    return [x + y for x, y in zip(a, b)]

    @overloaded
    def add(a: tx.Sequence[T], b: tx.Iterable[U]):
    return add(a, list(b))

    if __name__ == '__main__':
    print(add(3, 5))
    print()

    print(add(4.5, 8.2))
    print()

    print(add([1, 2, 3], 5.0))
    print()

    print(add([1, 2, 3], [1, 2, 3]))
    print(add([1, 2, 3], ["a", "b", "c"]))
    print()

    print(add([1, 2, 3], ["a", "b", "c"]))
    print()

    print(add([1, 2, 3], { "a", "b", "c" }))
    print()

    print(add([1, 2, 3], (x.upper() for x in ["a", "b", "c"])))
    print()

  2. fish2000 revised this gist Nov 20, 2018. 1 changed file with 10 additions and 7 deletions.
    17 changes: 10 additions & 7 deletions multiple_dispatch.py
    Original file line number Diff line number Diff line change
    @@ -20,8 +20,11 @@

    VERBOSE = True

    def nameof(arg):
    return arg.__name__

    def typename(arg):
    return type(arg).__name__
    return nameof(type(arg))

    def origin(arg):
    return getattr(arg, '__origin__', object)
    @@ -32,16 +35,16 @@ def is_generic_sequence(arg):
    def to_matchgroup(arg, groups):
    if type(arg) is tx.TypeVar:
    if arg in groups:
    return "(?P={})".format(arg.__name__)
    return "(?P={})".format(nameof(arg))
    else:
    groups |= { arg }
    return "(?P<{}>.*?)".format(arg.__name__)
    return "(?P<{}>.*?)".format(nameof(arg))
    else:
    return to_regex(arg, groups)

    def to_regex(typevar, groups):
    if typevar in { float, int, str }:
    return typevar.__name__
    return nameof(typevar)
    elif is_generic_sequence(typevar):
    return "(?:list|set|tuple)\[{}\]".format(to_matchgroup(typevar.__args__[0], groups))
    return ".*?"
    @@ -62,18 +65,18 @@ def to_callee(arg):
    t = typename(arg) + '[{}]'
    eltypes = get_element_types(arg)
    if len(eltypes) == 1:
    return t.format(list(eltypes)[0].__name__)
    return t.format(nameof(eltypes[0]))
    else:
    raise RuntimeError("Not implemented yet.")
    else:
    raise RuntimeError("Not implemented yet.")

    def to_match_target(caller_signature):
    return ", ".join([to_callee(el) for el in caller_signature])
    return ", ".join(to_callee(el) for el in caller_signature)

    def to_regex_sig(caller_signature):
    groups = set()
    return ", ".join([to_regex(el, groups) for el in caller_signature])
    return ", ".join(to_regex(el, groups) for el in caller_signature)

    class overloaded(object):

  3. fish2000 revised this gist Nov 20, 2018. 1 changed file with 48 additions and 19 deletions.
    67 changes: 48 additions & 19 deletions multiple_dispatch.py
    Original file line number Diff line number Diff line change
    @@ -1,36 +1,65 @@
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    #
    # multiple_dispatch.py
    #
    # My version:
    # https://gist.github.com/fish2000/1af4b852d20b7568a9b9c90fe2346b6d
    #
    # Forked from the originall by @wolfv:
    # https://gist.github.com/wolfv/73f56e4a9cac84eea6a796fde3213456
    #
    # * See below for usage notes

    import typing as tx
    import re
    import collections.abc

    __all__ = ('overloaded', 'T', 'U')
    __dir__ = lambda: list(__all__)

    VERBOSE = True

    def to_regex(typevar, groups):
    def to_matchgroup(arg, groups):
    if type(arg) is tx.TypeVar:
    if arg in groups:
    return "(?P={})".format(arg.__name__)
    else:
    groups |= {arg}
    return "(?P<{}>.*?)".format(arg.__name__)
    def typename(arg):
    return type(arg).__name__

    def origin(arg):
    return getattr(arg, '__origin__', object)

    def is_generic_sequence(arg):
    return origin(arg) is collections.abc.Sequence

    def to_matchgroup(arg, groups):
    if type(arg) is tx.TypeVar:
    if arg in groups:
    return "(?P={})".format(arg.__name__)
    else:
    return to_regex(arg, groups)
    groups |= { arg }
    return "(?P<{}>.*?)".format(arg.__name__)
    else:
    return to_regex(arg, groups)

    def to_regex(typevar, groups):
    if typevar in { float, int, str }:
    return typevar.__name__
    elif typevar.mro()[1] is tx.Sequence:
    elif is_generic_sequence(typevar):
    return "(?:list|set|tuple)\[{}\]".format(to_matchgroup(typevar.__args__[0], groups))
    return ".*?"

    def get_element_types(sequence):
    return set(type(el) for el in sequence)
    typeset = { type(el) for el in sequence }
    out = []
    for el in sequence:
    eltype = type(el)
    if eltype in typeset and eltype not in out:
    out.append(eltype)
    return out

    def to_callee(arg):
    if type(arg) in [float, int]:
    return type(arg).__name__
    elif type(arg) in [list, set, tuple]:
    t = type(arg).__name__ + '[{}]'
    if type(arg) in { float, int }:
    return typename(arg)
    elif type(arg) in { list, set, tuple }:
    t = typename(arg) + '[{}]'
    eltypes = get_element_types(arg)
    if len(eltypes) == 1:
    return t.format(list(eltypes)[0].__name__)
    @@ -81,14 +110,14 @@ def add(a: float, b: float):
    def add(a: tx.Sequence[T], b: float):
    return [x + b for x in a]

    @overloaded
    def add(a: tx.Sequence[T], b: tx.Sequence[T]):
    return [x + y for x, y in zip(a, b)]

    @overloaded
    def add(a: tx.Sequence[T], b: tx.Sequence[str]):
    return [str(x) + y for x, y in zip(a, b)]

    @overloaded
    def add(a: tx.Sequence[T], b: tx.Sequence[U]):
    return [x + y for x, y in zip(a, b)]

    if __name__ == '__main__':
    print(add(3, 5))
    print(add(4.5, 8.2))
  4. fish2000 revised this gist Nov 20, 2018. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion multiple_dispatch.py
    Original file line number Diff line number Diff line change
    @@ -52,7 +52,6 @@ class overloaded(object):

    def __init__(self, f):
    signature = tuple(x[1] for x in f.__annotations__.items())
    groups = set()
    self.fmap[to_regex_sig(signature)] = f

    def __call__(self, *args):
  5. fish2000 revised this gist Nov 20, 2018. 1 changed file with 5 additions and 2 deletions.
    7 changes: 5 additions & 2 deletions multiple_dispatch.py
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,7 @@
    __all__ = ('overloaded', 'T', 'U')
    __dir__ = lambda: list(__all__)

    VERBOSE = True

    def to_regex(typevar, groups):
    def to_matchgroup(arg, groups):
    @@ -57,9 +58,11 @@ def __init__(self, f):
    def __call__(self, *args):
    match_sig = to_match_target(args)
    for key, func in self.fmap.items():
    print("Matching: {} against\n {}\n".format(match_sig, key))
    if VERBOSE:
    print("Matching: {} against\n {}\n".format(match_sig, key))
    if (re.match(key, match_sig)):
    print(" === MATCH ===\n\n")
    if VERBOSE:
    print(" === MATCH ===\n\n")
    return func(*args)
    else:
    raise RuntimeError("No overload found for ", match_sig)
  6. fish2000 revised this gist Nov 20, 2018. 1 changed file with 13 additions and 9 deletions.
    22 changes: 13 additions & 9 deletions multiple_dispatch.py
    Original file line number Diff line number Diff line change
    @@ -1,9 +1,13 @@
    from typing import *
    import typing as tx
    import re

    __all__ = ('overloaded', 'T', 'U')
    __dir__ = lambda: list(__all__)


    def to_regex(typevar, groups):
    def to_matchgroup(arg, groups):
    if type(arg) is TypeVar:
    if type(arg) is tx.TypeVar:
    if arg in groups:
    return "(?P={})".format(arg.__name__)
    else:
    @@ -12,9 +16,9 @@ def to_matchgroup(arg, groups):
    else:
    return to_regex(arg, groups)

    if typevar in {float, int, str}:
    if typevar in { float, int, str }:
    return typevar.__name__
    elif typevar.mro()[1] is Sequence:
    elif typevar.mro()[1] is tx.Sequence:
    return "(?:list|set|tuple)\[{}\]".format(to_matchgroup(typevar.__args__[0], groups))
    return ".*?"

    @@ -68,19 +72,19 @@ def add(a: int, b: int):
    def add(a: float, b: float):
    return a + b

    T = TypeVar('T')
    U = TypeVar('U')
    T = tx.TypeVar('T')
    U = tx.TypeVar('U')

    @overloaded
    def add(a: Sequence[T], b: float):
    def add(a: tx.Sequence[T], b: float):
    return [x + b for x in a]

    @overloaded
    def add(a: Sequence[T], b: Sequence[T]):
    def add(a: tx.Sequence[T], b: tx.Sequence[T]):
    return [x + y for x, y in zip(a, b)]

    @overloaded
    def add(a: Sequence[T], b: Sequence[str]):
    def add(a: tx.Sequence[T], b: tx.Sequence[str]):
    return [str(x) + y for x, y in zip(a, b)]

    if __name__ == '__main__':
  7. @wolfv wolfv revised this gist Oct 24, 2018. No changes.
  8. @wolfv wolfv created this gist Oct 24, 2018.
    91 changes: 91 additions & 0 deletions multiple_dispatch.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,91 @@
    from typing import *
    import re

    def to_regex(typevar, groups):
    def to_matchgroup(arg, groups):
    if type(arg) is TypeVar:
    if arg in groups:
    return "(?P={})".format(arg.__name__)
    else:
    groups |= {arg}
    return "(?P<{}>.*?)".format(arg.__name__)
    else:
    return to_regex(arg, groups)

    if typevar in {float, int, str}:
    return typevar.__name__
    elif typevar.mro()[1] is Sequence:
    return "(?:list|set|tuple)\[{}\]".format(to_matchgroup(typevar.__args__[0], groups))
    return ".*?"

    def get_element_types(sequence):
    return set(type(el) for el in sequence)

    def to_callee(arg):
    if type(arg) in [float, int]:
    return type(arg).__name__
    elif type(arg) in [list, set, tuple]:
    t = type(arg).__name__ + '[{}]'
    eltypes = get_element_types(arg)
    if len(eltypes) == 1:
    return t.format(list(eltypes)[0].__name__)
    else:
    raise RuntimeError("Not implemented yet.")
    else:
    raise RuntimeError("Not implemented yet.")

    def to_match_target(caller_signature):
    return ", ".join([to_callee(el) for el in caller_signature])

    def to_regex_sig(caller_signature):
    groups = set()
    return ", ".join([to_regex(el, groups) for el in caller_signature])

    class overloaded(object):

    fmap = {}

    def __init__(self, f):
    signature = tuple(x[1] for x in f.__annotations__.items())
    groups = set()
    self.fmap[to_regex_sig(signature)] = f

    def __call__(self, *args):
    match_sig = to_match_target(args)
    for key, func in self.fmap.items():
    print("Matching: {} against\n {}\n".format(match_sig, key))
    if (re.match(key, match_sig)):
    print(" === MATCH ===\n\n")
    return func(*args)
    else:
    raise RuntimeError("No overload found for ", match_sig)

    @overloaded
    def add(a: int, b: int):
    return a + b + 100

    @overloaded
    def add(a: float, b: float):
    return a + b

    T = TypeVar('T')
    U = TypeVar('U')

    @overloaded
    def add(a: Sequence[T], b: float):
    return [x + b for x in a]

    @overloaded
    def add(a: Sequence[T], b: Sequence[T]):
    return [x + y for x, y in zip(a, b)]

    @overloaded
    def add(a: Sequence[T], b: Sequence[str]):
    return [str(x) + y for x, y in zip(a, b)]

    if __name__ == '__main__':
    print(add(3, 5))
    print(add(4.5, 8.2))
    print(add([1, 2, 3], 5.0))
    print(add([1, 2, 3], [1, 2, 3]))
    print(add([1, 2, 3], ["a", "b", "c"]))