Last active
September 27, 2016 16:38
-
-
Save cwalv/82558e3e10504125ce94a6fb553489cc to your computer and use it in GitHub Desktop.
python's `__init__` and `__del__` vs `__enter__` and `__exit__` methods
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
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"collapsed": true | |
}, | |
"source": [ | |
"## `__del__` vs. `__exit__`\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG:root:test\n" | |
] | |
} | |
], | |
"source": [ | |
"import logging\n", | |
"rl = logging.getLogger()\n", | |
"rl.setLevel(logging.DEBUG)\n", | |
"logging.debug(\"test\")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"class Thing(object): # Basically always want to explicitly inherit from `object` in Python 2\n", | |
" \n", | |
" def __init__(self, tid):\n", | |
" self.tid = tid\n", | |
" logging.info(\"Thing __init__'d: {}\".format(self))\n", | |
" \n", | |
" def __del__(self):\n", | |
" logging.info(\"Thing __del__'d: {}\".format(self))\n", | |
" \n", | |
" def __enter__(self):\n", | |
" logging.info(\"Thing __enter__'ing: {}\".format(self))\n", | |
" return self\n", | |
" \n", | |
" def __exit__(self, type, value, traceback):\n", | |
" logging.info(\"Thing __exit__'ing: {}\\n exception info: ({})\".format(\n", | |
" self, (type, value, traceback)))\n", | |
" \n", | |
" def __str__(self):\n", | |
" return \"Thing(tid={}) @ {}\".format(self.__dict__.get(\"tid\", \"<not set>\"), id(self))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __init__'d: Thing(tid=1) @ 64515312\n" | |
] | |
} | |
], | |
"source": [ | |
"t1=Thing(1)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Here, we call `t1.__enter__` by using `t1` in a `with` statement; the `enterRetVal` variable that references the return value from `t1.__enter__` is not deleted at the end of the `with` block, but `t1`'s `__exit__` method is called:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __enter__'ing: Thing(tid=1) @ 64515312\n", | |
"INFO:root:t1 is enterRetVal: True\n", | |
"INFO:root:Thing __exit__'ing: Thing(tid=1) @ 64515312\n", | |
" exception info: ((None, None, None))\n" | |
] | |
} | |
], | |
"source": [ | |
"with t1 as enterRetVal:\n", | |
" logging.info(\"t1 is enterRetVal: {}\".format(t1 is enterRetVal))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"True" | |
] | |
}, | |
"execution_count": 5, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"enterRetVal is t1 # `enterRetVal` is still in scope, and still references the same that `t1` does:" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"calling `del` on `enterRetVal` or `t1` will decrement the reference count on `Thing(tid=1)`, but it won't be collected because there's still another reference" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"del enterRetVal" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"calling `del` on `t1` now will cause `Thing(tid=1)` to be collected:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __del__'d: Thing(tid=1) @ 64515312\n" | |
] | |
} | |
], | |
"source": [ | |
"del t1" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"even tho there are no more references to `Thing(tid=1)`, we may not see the logging message from its `__del__` method yet: nothing in the spec says that `__del__` has to be called right away (and in other python implementations like IronPython, it's never called right away), and in some cases it's never called at all: https://docs.python.org/2/reference/datamodel.html#object.__del__" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def doStuff():\n", | |
" t2 = Thing(2)\n", | |
" with t2 as enterRetVal:\n", | |
" logging.info(\"t2 is enterRetVal: {}\".format(t2 is enterRetVal))\n", | |
" # Things will be cleaned up automatically when they go out of scope .." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __init__'d: Thing(tid=2) @ 44176208\n", | |
"INFO:root:Thing __enter__'ing: Thing(tid=2) @ 44176208\n", | |
"INFO:root:t2 is enterRetVal: True\n", | |
"INFO:root:Thing __exit__'ing: Thing(tid=2) @ 44176208\n", | |
" exception info: ((None, None, None))\n", | |
"INFO:root:Thing __del__'d: Thing(tid=2) @ 44176208\n" | |
] | |
} | |
], | |
"source": [ | |
"doStuff()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def doMoreStuff():\n", | |
" with Thing(3) as enterRetVal:\n", | |
" logging.info(\"only 1 reference to {} (i.e., `enterRetVal`)\".format(enterRetVal))\n", | |
" # Things will be cleaned up automatically when they go out of scope .." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __init__'d: Thing(tid=3) @ 44176816\n", | |
"INFO:root:Thing __enter__'ing: Thing(tid=3) @ 44176816\n", | |
"INFO:root:only 1 reference to Thing(tid=3) @ 44176816 (i.e., `enterRetVal`)\n", | |
"INFO:root:Thing __exit__'ing: Thing(tid=3) @ 44176816\n", | |
" exception info: ((None, None, None))\n", | |
"INFO:root:Thing __del__'d: Thing(tid=3) @ 44176816\n" | |
] | |
} | |
], | |
"source": [ | |
"doMoreStuff()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def doStuff4():\n", | |
" with Thing(4) as enterRetVal:\n", | |
" logging.info(\"only 1 reference to {} (i.e., `enterRetVal`)\".format(enterRetVal))\n", | |
" return enterRetVal" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 13, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __init__'d: Thing(tid=4) @ 44175856\n", | |
"INFO:root:Thing __enter__'ing: Thing(tid=4) @ 44175856\n", | |
"INFO:root:only 1 reference to Thing(tid=4) @ 44175856 (i.e., `enterRetVal`)\n", | |
"INFO:root:Thing __exit__'ing: Thing(tid=4) @ 44175856\n", | |
" exception info: ((None, None, None))\n" | |
] | |
} | |
], | |
"source": [ | |
"t4 = doStuff4()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 14, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:t4 references Thing(tid=4) @ 44175856\n" | |
] | |
} | |
], | |
"source": [ | |
"logging.info(\"t4 references {}\".format(t4))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 15, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __enter__'ing: Thing(tid=4) @ 44175856\n", | |
"INFO:root:Thing __exit__'ing: Thing(tid=4) @ 44175856\n", | |
" exception info: ((None, None, None))\n" | |
] | |
} | |
], | |
"source": [ | |
"with t4:\n", | |
" pass" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __del__'d: Thing(tid=4) @ 44175856\n" | |
] | |
} | |
], | |
"source": [ | |
"del t4" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 17, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:nothing referencing `Thing(tid=4)` anymore ..\n" | |
] | |
} | |
], | |
"source": [ | |
"logging.info(\"nothing referencing `Thing(tid=4)` anymore ..\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Sometimes `__del__` can be called even tho `__init__` hasn't been called; `__exit__` can never be called without `__enter__`:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 18, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:root:Thing __del__'d: Thing(tid=<not set>) @ 44176720\n" | |
] | |
}, | |
{ | |
"ename": "TypeError", | |
"evalue": "__init__() takes exactly 2 arguments (1 given)", | |
"output_type": "error", | |
"traceback": [ | |
"\u001b[0;31m\u001b[0m", | |
"\u001b[0;31mTypeError\u001b[0mTraceback (most recent call last)", | |
"\u001b[0;32m<ipython-input-18-48b865074d18>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mt\u001b[0m\u001b[1;33m=\u001b[0m\u001b[0mThing\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", | |
"\u001b[0;31mTypeError\u001b[0m: __init__() takes exactly 2 arguments (1 given)" | |
] | |
} | |
], | |
"source": [ | |
"t=Thing()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": true | |
}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 2", | |
"language": "python", | |
"name": "python2" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 2 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython2", | |
"version": "2.7.10" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 1 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment