Skip to content

Instantly share code, notes, and snippets.

@shhommychon
Created June 27, 2021 08:24
Show Gist options
  • Select an option

  • Save shhommychon/41cdadc861f7516ad96463add505e89a to your computer and use it in GitHub Desktop.

Select an option

Save shhommychon/41cdadc861f7516ad96463add505e89a to your computer and use it in GitHub Desktop.
(pecab) Single vs Multiple Dispatch 정리
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "SingleVSMultipleDispatch.ipynb",
"provenance": [],
"collapsed_sections": [],
"toc_visible": true,
"authorship_tag": "ABX9TyNG8CAQDasOrwoquYtliybm"
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "nEStEfu2GfC5"
},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/gist/FeetCodingHommy/41cdadc861f7516ad96463add505e89a)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "CQmWK3tN5YFT"
},
"source": [
"# Dynamic Dispatching vs Method Overloading\n",
" * 파이썬과 같은 Dynamic Programming에서는 Overloading과 유사한 Dynamic Dispatching\\[출처: [위키](https://en.wikipedia.org/wiki/Dynamic_dispatch)\\]을 사용할 수 있다고 합니다.\n",
" * 메소드의 Overloading은 컴파일 단계에서 이루어지지만, Dynamic Dispatching은 런타임 때 이루어집니다. \\[출처: [위키](https://stackoverflow.com/questions/1801216/what-is-the-difference-between-multiple-dispatch-and-method-overloading)\\]\n",
" * \\[출처: [미디엄](https://towardsdatascience.com/the-correct-way-to-overload-functions-in-python-b11b50ca7336)\\]에 의하면, Python에서는 기본 라이브러리 `functools`에 구현되어 있는 Single Dispatch와, 별도의 설치가 필요한 Multiple Dispatch를 이용할 수 있다고 합니다. (해당 글의 내용에서 중요한 내용과 코드 예시를 참고하여 아래 내용을 작성하였습니다.)\n",
"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "o4sG7Is47Bhx"
},
"source": [
"## `functools`의 `@singledispatch`\n",
" * 파이썬 기본 라이브러리 `functools`에 탑재되어 별도의 설치가 필요하지 않다는 장점이 있습니다.\n",
" * Python 3.4부터 추가된 기능이며 \\[출처: [다큐먼트](https://docs.python.org/ko/3/library/functools.html#functools.singledispatch)\\] Python 3.7에서 `.register()` 어트리뷰트의 기능이 업데이트 되었습니다.\n",
" * 단점으로, Single Dispatch에서는 하나의 argument만 오버로딩이 가능합니다.\n",
" * 여러 argument들의 오버로딩을 시도하였을 때에는 오류가 발생한다는 것을 아래 코드에서 확인하실 수 있습니다.\n",
" * 첫번째 argument의 타입만을 확인하는 것으로 사료됩니다.\n",
" * 또다른 단점으로는, 클래스 메소드에서는 사용 불가능합니다.\n",
" * 클래스 메소드에서는 `functools` 내 별도의 `@singledispatchmethod`를 사용해야합니다."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "lRG76Anp_Ls0"
},
"source": [
"### 일반 함수에서의 `functools.singledispatch` 사용"
]
},
{
"cell_type": "code",
"metadata": {
"id": "OywxNqS492Ov"
},
"source": [
"from functools import singledispatch\n",
"\n",
"@singledispatch\n",
"def add(a, b):\n",
" raise NotImplementedError\n",
"\n",
"@add.register\n",
"def _(a: int, b: int):\n",
" return a + b\n",
"\n",
"@add.register\n",
"def _(a: str, b: str):\n",
" return int(a) + int(b)\n",
"\n",
"@add.register\n",
"def _(c: str):\n",
" a, b = c.split('+')\n",
" return int(a.strip()) + int(b.strip())"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "7zpLWZ_B95pB",
"executionInfo": {
"status": "ok",
"timestamp": 1623650103180,
"user_tz": -540,
"elapsed": 5,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "a9bed5eb-300e-402f-b758-d53cb385f8ea"
},
"source": [
"add(1, 2)"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 2
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 298
},
"id": "riTjVMf4-ekp",
"executionInfo": {
"status": "error",
"timestamp": 1623650104663,
"user_tz": -540,
"elapsed": 234,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "f36d0277-d110-4d6b-e18c-bc869e41ceb3"
},
"source": [
"add('1' + '2') # 에러 발생, 3번째 dispatch에 의해 덮어씌워진 것으로 예상됩니다."
],
"execution_count": null,
"outputs": [
{
"output_type": "error",
"ename": "ValueError",
"evalue": "ignored",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mValueError\u001B[0m Traceback (most recent call last)",
"\u001B[0;32m<ipython-input-3-8fa95352fbbd>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0madd\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m'1'\u001B[0m \u001B[0;34m+\u001B[0m \u001B[0;34m'2'\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;31m# 에러 발생, 3번째 dispatch에 의해 덮어씌워진 것으로 예상됩니다.\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m",
"\u001B[0;32m/usr/lib/python3.7/functools.py\u001B[0m in \u001B[0;36mwrapper\u001B[0;34m(*args, **kw)\u001B[0m\n\u001B[1;32m 838\u001B[0m '1 positional argument')\n\u001B[1;32m 839\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 840\u001B[0;31m \u001B[0;32mreturn\u001B[0m \u001B[0mdispatch\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__class__\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkw\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 841\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 842\u001B[0m \u001B[0mfuncname\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mgetattr\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m'__name__'\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m'singledispatch function'\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;32m<ipython-input-1-8280fe2bd9e3>\u001B[0m in \u001B[0;36m_\u001B[0;34m(c)\u001B[0m\n\u001B[1;32m 15\u001B[0m \u001B[0;34m@\u001B[0m\u001B[0madd\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mregister\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 16\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0m_\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mc\u001B[0m\u001B[0;34m:\u001B[0m \u001B[0mstr\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 17\u001B[0;31m \u001B[0ma\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mb\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mc\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0msplit\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m'+'\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 18\u001B[0m \u001B[0;32mreturn\u001B[0m \u001B[0mint\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0ma\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mstrip\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;34m+\u001B[0m \u001B[0mint\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mb\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mstrip\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;31mValueError\u001B[0m: not enough values to unpack (expected 2, got 1)"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "Nz8QqZ3i-iDa",
"executionInfo": {
"status": "ok",
"timestamp": 1623650105557,
"user_tz": -540,
"elapsed": 6,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "8e2977b9-802c-4c4a-c8d8-b1eedd964d3e"
},
"source": [
"add('1+2')"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 4
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "Z8uBgTkl_-lo"
},
"source": [
"# 파이썬 3.7부터는 아래의 문법이 가능합니다.\n",
"\n",
"@singledispatch\n",
"def double_number(a):\n",
" raise NotImplementedError\n",
"\n",
"@double_number.register(int)\n",
"def _(a):\n",
" return 2 * a\n",
"\n",
"@double_number.register(str)\n",
"def _(a):\n",
" return 2 * int(a)"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "MIH82qDjAKha",
"executionInfo": {
"status": "ok",
"timestamp": 1623650110660,
"user_tz": -540,
"elapsed": 7,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "53fa392c-d4f1-4af1-a1ad-36d9f2c968a9"
},
"source": [
"double_number(2)"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"4"
]
},
"metadata": {
"tags": []
},
"execution_count": 6
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "BqVMNC1YANsB",
"executionInfo": {
"status": "ok",
"timestamp": 1623650111020,
"user_tz": -540,
"elapsed": 365,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "9b50c9ee-b94d-49f0-9e31-1370cc2e910a"
},
"source": [
"double_number('2')"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"4"
]
},
"metadata": {
"tags": []
},
"execution_count": 7
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 239
},
"id": "IXdLLk8X_nrU",
"executionInfo": {
"status": "error",
"timestamp": 1623650111021,
"user_tz": -540,
"elapsed": 9,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "143a460e-169d-4a50-dd3d-208d422f2e76"
},
"source": [
"# 단, 해당 문법은 중복된 argument의 형을 받아들일 수 없습니다.\n",
"# (하나의 argument만 dynamic dispatching이 가능합니다.)\n",
"\n",
"@singledispatch\n",
"def add(a, b):\n",
" raise NotImplementedError\n",
"\n",
"@add.register(int, int) # 에러 발생, 여러 타입을 받지 못하는 것으로 예상됩니다.\n",
"def _(a, b):\n",
" return a + b\n",
"\n",
"@add.register(str, str)\n",
"def _(a, b):\n",
" return int(a) + int(b)\n",
"\n",
"@add.register(str)\n",
"def _(c):\n",
" a, b = c.split('+')\n",
" return int(a.strip()) + int(b.strip())"
],
"execution_count": null,
"outputs": [
{
"output_type": "error",
"ename": "TypeError",
"evalue": "ignored",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)",
"\u001B[0;32m<ipython-input-8-70a67ab10a61>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m()\u001B[0m\n\u001B[1;32m 6\u001B[0m \u001B[0;32mraise\u001B[0m \u001B[0mNotImplementedError\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 7\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 8\u001B[0;31m \u001B[0;34m@\u001B[0m\u001B[0madd\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mregister\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mint\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mint\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;31m# 에러 발생, 여러 타입을 받지 못하는 것으로 예상됩니다.\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 9\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0m_\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0ma\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mb\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 10\u001B[0m \u001B[0;32mreturn\u001B[0m \u001B[0ma\u001B[0m \u001B[0;34m+\u001B[0m \u001B[0mb\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;31mTypeError\u001B[0m: int() argument must be a string, a bytes-like object or a number, not 'function'"
]
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "OXZPlwJv-k_s"
},
"source": [
"### 클래스 메소드에서의 `functools.singledispatch` 사용"
]
},
{
"cell_type": "code",
"metadata": {
"id": "mkt02m2g3D2z"
},
"source": [
"from functools import singledispatch\n",
"\n",
"class AdderSingleDispatch:\n",
" @singledispatch\n",
" def add(self, a, b):\n",
" raise NotImplementedError\n",
" \n",
" @add.register\n",
" def _(self, a: int, b: int):\n",
" return a + b\n",
" \n",
" @add.register\n",
" def _(self, a: str, b: str):\n",
" return int(a) + int(b)\n",
" \n",
" @add.register\n",
" def _(self, c: str):\n",
" a, b = c.split('+')\n",
" return int(a.strip()) + int(b.strip())"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "07DwObMS3LFW"
},
"source": [
"a1 = AdderSingleDispatch()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 316
},
"id": "g7wnvynO3N39",
"executionInfo": {
"status": "error",
"timestamp": 1623650115827,
"user_tz": -540,
"elapsed": 5,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "38235973-4d34-496b-d12a-4995032c470c"
},
"source": [
"a1.add(1, 2) # 에러 발생, @singledispatch는 클래스 메소드에서 사용할 수 없습니다."
],
"execution_count": null,
"outputs": [
{
"output_type": "error",
"ename": "NotImplementedError",
"evalue": "ignored",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mNotImplementedError\u001B[0m Traceback (most recent call last)",
"\u001B[0;32m<ipython-input-11-9ef9dfc3bae1>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0ma1\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;36m1\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;36m2\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;31m# 에러 발생, @singledispatch는 클래스 메소드에서 사용할 수 없습니다.\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m",
"\u001B[0;32m/usr/lib/python3.7/functools.py\u001B[0m in \u001B[0;36mwrapper\u001B[0;34m(*args, **kw)\u001B[0m\n\u001B[1;32m 838\u001B[0m '1 positional argument')\n\u001B[1;32m 839\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 840\u001B[0;31m \u001B[0;32mreturn\u001B[0m \u001B[0mdispatch\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__class__\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkw\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 841\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 842\u001B[0m \u001B[0mfuncname\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mgetattr\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mfunc\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m'__name__'\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m'singledispatch function'\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;32m<ipython-input-9-e483bc8f13c0>\u001B[0m in \u001B[0;36madd\u001B[0;34m(self, a, b)\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[0;34m@\u001B[0m\u001B[0msingledispatch\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 5\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0madd\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0ma\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mb\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 6\u001B[0;31m \u001B[0;32mraise\u001B[0m \u001B[0mNotImplementedError\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 7\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 8\u001B[0m \u001B[0;34m@\u001B[0m\u001B[0madd\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mregister\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;31mNotImplementedError\u001B[0m: "
]
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "jQehjCSC5Xkp"
},
"source": [
"## `functools`의 `@singledispatchmethod`\n",
" * `@singledispatch`와 마찬가지로 파이썬 기본 라이브러리 `functools`에 탑재되어 별도의 설치가 필요하지 않다는 장점이 있습니다.\n",
" * 단, 해당 기능은 Python 3.8부터 추가된 기능이며 \\[출처: [다큐먼트](https://docs.python.org/ko/3/library/functools.html#functools.singledispatchmethod)\\] Python 3.7 이하에서는 별도의 설치 없이는 사용할 수 없습니다.\n",
" * 단점으로, `@singledispatch`와 마찬가지로 Single Dispatch의 구현체이기 때문에 하나의 argument만 오버로딩이 가능합니다.\n",
" * 여러 argument들의 오버로딩을 시도하였을 때에는 오류가 발생한다는 것을 아래 코드에서 확인하실 수 있습니다."
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "XkwhtpJL1J-F",
"executionInfo": {
"status": "ok",
"timestamp": 1623650117871,
"user_tz": -540,
"elapsed": 214,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "bc1eb2f2-8b1b-433a-e1bd-babff54e2554"
},
"source": [
"!python --version # Google Colab의 파이썬 버전은 (21년 6월 기준) 3.7.10이기 때문에 별도의 설치가 필요합니다."
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"text": [
"Python 3.7.10\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "YjmdB8KR39zR",
"executionInfo": {
"status": "ok",
"timestamp": 1623650126452,
"user_tz": -540,
"elapsed": 7664,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "b2ee3dc1-4471-4b01-f5df-bae2c07dd909"
},
"source": [
"!pip install singledispatchmethod"
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"text": [
"Collecting singledispatchmethod\n",
" Downloading https://files.pythonhosted.org/packages/be/e0/20ecc21453d7f052ee04d0d9b3305798e0f6afc619885c9aaee4ba86b25c/singledispatchmethod-1.0.tar.gz\n",
"Building wheels for collected packages: singledispatchmethod\n",
" Building wheel for singledispatchmethod (setup.py) ... \u001B[?25l\u001B[?25hdone\n",
" Created wheel for singledispatchmethod: filename=singledispatchmethod-1.0-cp37-none-any.whl size=4682 sha256=81cd0fcb0756ff94f9e461e76f2f63f07b793a7a4dc2693f2fbfd3a6c2feb76c\n",
" Stored in directory: /root/.cache/pip/wheels/6e/e9/c8/63b765bcd87038068c26be2cb27337299b6cc8ad4dd91c1e3f\n",
"Successfully built singledispatchmethod\n",
"Installing collected packages: singledispatchmethod\n",
"Successfully installed singledispatchmethod-1.0\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4KCaK29BCr32"
},
"source": [
"### 클래스 메소드에서의 `functools.singledispatchmethod` 사용"
]
},
{
"cell_type": "code",
"metadata": {
"id": "CxRqRjsJ3Q6j"
},
"source": [
"# from functools import singledispatchmethod # 파이썬 3.8부터 사용 가능합니다.\n",
"from singledispatchmethod import singledispatchmethod\n",
"\n",
"class AdderSingleDispatchMethod:\n",
" @singledispatchmethod\n",
" def add(self, a, b):\n",
" raise NotImplementedError\n",
" \n",
" @add.register\n",
" def _(self, a: int, b: int):\n",
" return a + b\n",
" \n",
" @add.register\n",
" def _(self, a: str, b: str):\n",
" return int(a) + int(b)\n",
" \n",
" @add.register\n",
" def _(self, c: str):\n",
" a, b = c.split('+')\n",
" return int(a.strip()) + int(b.strip())"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "tX8gE7VN4dnN"
},
"source": [
"a2 = AdderSingleDispatchMethod()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "zpeiQl7b4gqn",
"executionInfo": {
"status": "ok",
"timestamp": 1623650131200,
"user_tz": -540,
"elapsed": 5,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "a1b21e66-66be-454a-9fd1-cbe57ca918bf"
},
"source": [
"a2.add(1, 2)"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 16
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 292
},
"id": "vVS-PGwz4ixk",
"executionInfo": {
"status": "error",
"timestamp": 1623650132746,
"user_tz": -540,
"elapsed": 361,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "4d6d8054-8a6a-482c-ca02-86fad18612d6"
},
"source": [
"a2.add('1', '2') # 에러 발생, 3번째 dispatch에 의해 덮어씌워진 것으로 예상됩니다."
],
"execution_count": null,
"outputs": [
{
"output_type": "error",
"ename": "TypeError",
"evalue": "ignored",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)",
"\u001B[0;32m<ipython-input-17-f8ef500ec101>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0ma2\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m'1'\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m'2'\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;31m# 에러 발생, 3번째 dispatch에 의해 덮어씌워진 것으로 예상됩니다.\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m",
"\u001B[0;32m/usr/local/lib/python3.7/dist-packages/singledispatchmethod.py\u001B[0m in \u001B[0;36m_method\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 67\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0m_method\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 68\u001B[0m \u001B[0mmethod\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mdispatcher\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mdispatch\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__class__\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 69\u001B[0;31m \u001B[0;32mreturn\u001B[0m \u001B[0mmethod\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__get__\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mobj\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mcls\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 70\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 71\u001B[0m \u001B[0m_method\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__isabstractmethod__\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__isabstractmethod__\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;31mTypeError\u001B[0m: _() takes 2 positional arguments but 3 were given"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "9mw2uQYq4oa4",
"executionInfo": {
"status": "ok",
"timestamp": 1623650133981,
"user_tz": -540,
"elapsed": 201,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "a483b124-7df5-4e58-b281-e1b8939de1f8"
},
"source": [
"a2.add('1+2')"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 18
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "obAwFXtQ4uJx"
},
"source": [
"from singledispatchmethod import singledispatchmethod\n",
"\n",
"class AdderSingleDispatchMethod2:\n",
" @singledispatchmethod\n",
" def add(self, a, b):\n",
" raise NotImplementedError\n",
" \n",
" # 아래 두 dispatch의 내용을 바꾸어 보았습니다.\n",
" @add.register\n",
" def _(self, a: str, b: int):\n",
" return int(a) + b\n",
" \n",
" @add.register\n",
" def _(self, a: int, b: str):\n",
" return a + int(b)\n",
" # -------------------- #\n",
" \n",
" @add.register\n",
" def _(self, c: str):\n",
" a, b = c.split('+')\n",
" return int(a.strip()) + int(b.strip())"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "tM2HcqbU40YE"
},
"source": [
"a2_1 = AdderSingleDispatchMethod2()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 292
},
"id": "tbn1IXptD8wc",
"executionInfo": {
"status": "error",
"timestamp": 1623650139657,
"user_tz": -540,
"elapsed": 222,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "faee974c-de34-4fb2-ac36-56efc96df48a"
},
"source": [
"a2_1.add('1', 2) # 에러 발생, 3번째 dispatch에 의해 덮어씌워진 것으로 예상됩니다."
],
"execution_count": null,
"outputs": [
{
"output_type": "error",
"ename": "TypeError",
"evalue": "ignored",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mTypeError\u001B[0m Traceback (most recent call last)",
"\u001B[0;32m<ipython-input-21-19a5e2a9f729>\u001B[0m in \u001B[0;36m<module>\u001B[0;34m()\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0ma2_1\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m'1'\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;36m2\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;31m# 에러 발생, 3번째 dispatch에 의해 덮어씌워진 것으로 예상됩니다.\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m",
"\u001B[0;32m/usr/local/lib/python3.7/dist-packages/singledispatchmethod.py\u001B[0m in \u001B[0;36m_method\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 67\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0m_method\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 68\u001B[0m \u001B[0mmethod\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mdispatcher\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mdispatch\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__class__\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 69\u001B[0;31m \u001B[0;32mreturn\u001B[0m \u001B[0mmethod\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__get__\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mobj\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mcls\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 70\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 71\u001B[0m \u001B[0m_method\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__isabstractmethod__\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m__isabstractmethod__\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;31mTypeError\u001B[0m: _() takes 2 positional arguments but 3 were given"
]
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "F2_J4xAb427G",
"executionInfo": {
"status": "ok",
"timestamp": 1623650141564,
"user_tz": -540,
"elapsed": 232,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "0fa12182-4eeb-4e67-9ee9-43fefd16ae11"
},
"source": [
"a2_1.add(1, '2')"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 22
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "EltjXaDd5Ble",
"executionInfo": {
"status": "ok",
"timestamp": 1623650143081,
"user_tz": -540,
"elapsed": 232,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "f4b8409b-bbd0-434e-a456-7456b42d418f"
},
"source": [
"a2_1.add('1+2')"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 23
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "FcX2uJNYB0lm"
},
"source": [
"## `multipledispatch`의 `@dispatch`\n",
" * 별도의 설치가 필요하다는 단점이 있습니다. (`pip install multipledispatch`)\n",
" * 하지만 장점으로, Single Dispatch과는 달리 여러 개의 argument를 오버로딩 가능합니다. \\[출처: [다큐먼트](https://multiple-dispatch.readthedocs.io/en/latest/resolution.html#multiple-inputs)\\]"
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "OMi9lEo527jK",
"executionInfo": {
"status": "ok",
"timestamp": 1623650149324,
"user_tz": -540,
"elapsed": 3847,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "f44205bc-4bc0-46b0-e9a1-bc62df8e2ca8"
},
"source": [
"!pip install multipledispatch # Google Colab에 설치되어 있지 않아 별도의 설치를 진행합니다."
],
"execution_count": null,
"outputs": [
{
"output_type": "stream",
"text": [
"Collecting multipledispatch\n",
" Downloading https://files.pythonhosted.org/packages/89/79/429ecef45fd5e4504f7474d4c3c3c4668c267be3370e4c2fd33e61506833/multipledispatch-0.6.0-py3-none-any.whl\n",
"Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from multipledispatch) (1.15.0)\n",
"Installing collected packages: multipledispatch\n",
"Successfully installed multipledispatch-0.6.0\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Dnbtdf2AFcRo"
},
"source": [
"### 클래스 메소드에서의 `multipledispatch.dispatch` 사용"
]
},
{
"cell_type": "code",
"metadata": {
"id": "LHI_DizU5Lkt"
},
"source": [
"from multipledispatch import dispatch\n",
"\n",
"class AdderMultipleDispatch:\n",
" @dispatch(int, int)\n",
" def add(self, a, b):\n",
" return a + b\n",
" \n",
" @dispatch(str, str)\n",
" def add(self, a, b):\n",
" return int(a) + int(b)\n",
" \n",
" @dispatch(str)\n",
" def add(self, c):\n",
" a, b = c.split('+')\n",
" return int(a.strip()) + int(b.strip())"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "sc8-35aV5NEU"
},
"source": [
"a3 = AdderMultipleDispatch()"
],
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "lpT0oPZT5PXk",
"executionInfo": {
"status": "ok",
"timestamp": 1623650149326,
"user_tz": -540,
"elapsed": 8,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "bb39e761-f156-4a2a-965e-3e9a5e08160c"
},
"source": [
"a3.add(1, 2)"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 27
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "lYh9E6Ou5PVO",
"executionInfo": {
"status": "ok",
"timestamp": 1623650150302,
"user_tz": -540,
"elapsed": 3,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "3aed2a5f-915b-47d5-e5a1-61a10913c241"
},
"source": [
"a3.add('1', '2') # 이전 Single Dispatch의 구현체와는 다르게 오류가 발생하지 않습니다."
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 28
}
]
},
{
"cell_type": "code",
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "jU_8u0kM5PN8",
"executionInfo": {
"status": "ok",
"timestamp": 1623650151690,
"user_tz": -540,
"elapsed": 221,
"user": {
"displayName": "",
"photoUrl": "https://lh3.googleusercontent.com/a-/AOh14Gi6asJRxrkHJ_WJj6icS2AfG9mqIVMUx6YCpaaWKA=s64",
"userId": "00000000000000000000"
}
},
"outputId": "6572756e-7a08-41b1-df10-88334792fa5b"
},
"source": [
"a3.add('1+2')"
],
"execution_count": null,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"3"
]
},
"metadata": {
"tags": []
},
"execution_count": 29
}
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment