diff --git a/README.md b/README.md index 986f1b8..de3e4d0 100644 --- a/README.md +++ b/README.md @@ -100,16 +100,26 @@ for subclass in (float, list, list[float], tuple[int]): assert not issubclass(subclass, cls) ``` -If a type implements a custom `__instancecheck__`, it can opt-in to dispatch (without caching) by registering its metaclass and bases with `subtype.origins`. `parametric` provides a convenient constructor, with support for predicate functions and checking attributes. +If a type implements a custom `__instancecheck__`, it can opt-in to dispatch (without caching) by registering its metaclass and bases with `subtype.origins`. `parametric` provides a convenient constructor, which will match the base class, predicate functions, and check attributes. ```python from multimethod import parametric -coro = parametric(Callable, asyncio.iscoroutinefunction) -ints = parametric(array, typecode='i') +Coroutine = parametric(Callable, asyncio.iscoroutinefunction) +IntArray = parametric(array, typecode='i') ``` -`overload` used to dispatch on annotated predicate functions. It is deprecated because a custom instance check - including using `parametric` - offers the same functionality. +`overload` used to dispatch on annotated predicate functions. It is deprecated because a custom instance check - including using `parametric` - offers the same functionality. Any predicate function can be wrapped with the closest matching base class, including `object` if necessary. + +```python +Cls = parametric(object, predicate) +Digits = parametric(str, str.isdigit) +assert isinstance('0', Digits) +assert not isinstance('a', Digits) + +@meth.register +def _(arg: Digits): ... +``` ### classes `classmethod` and `staticmethod` may be used with a multimethod, but must be applied _last_, i.e., wrapping the final multimethod definition after all functions are registered. For class and instance methods, `cls` and `self` participate in the dispatch as usual. They may be left blank when using annotations, otherwise use `object` as a placeholder. diff --git a/docs/examples.ipynb b/docs/examples.ipynb index 28c9266..b8efafe 100644 --- a/docs/examples.ipynb +++ b/docs/examples.ipynb @@ -133,7 +133,7 @@ "metadata": {}, "source": [ "## parametric\n", - "In addition to `issubclass`, multimethods can dispatch on `isinstance` with additional parametric checks." + "In addition to `issubclass`, multimethods can dispatch on `isinstance` with parametric checks." ] }, { @@ -148,7 +148,7 @@ "from concurrent import futures\n", "from multimethod import parametric\n", "\n", - "coro = parametric(Callable, asyncio.iscoroutinefunction)\n", + "Coroutine = parametric(Callable, asyncio.iscoroutinefunction)\n", "\n", "\n", "@multimethod\n", @@ -157,7 +157,7 @@ "\n", "\n", "@multimethod\n", - "async def wait(timeout, func: coro, *args):\n", + "async def wait(timeout, func: Coroutine, *args):\n", " return await asyncio.wait_for(func(*args), timeout)\n", "\n", "\n", @@ -173,6 +173,27 @@ "wait(0.5, asyncio.sleep, 0.01)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from array import array\n", + "\n", + "IntArray = parametric(array, typecode='i')\n", + "isinstance(array('i'), IntArray)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "isinstance(array('f'), IntArray)" + ] + }, { "cell_type": "markdown", "metadata": {},