{"id":7645,"date":"2022-05-31T13:04:58","date_gmt":"2022-05-31T20:04:58","guid":{"rendered":"https:\/\/coderpad.io\/?p=7645"},"modified":"2023-06-05T14:26:09","modified_gmt":"2023-06-05T21:26:09","slug":"guide-to-python-magic-methods","status":"publish","type":"post","link":"https:\/\/coderpad.io\/blog\/development\/guide-to-python-magic-methods\/","title":{"rendered":"A Guide to Python&#8217;s Secret Superpower: Magic Methods"},"content":{"rendered":"\n<p>Python has a secret superpower with a similarly stupendous name: Magic Methods. These methods can fundamentally change the way you code with Python classes and introduce code that seems \u2728 magical \u2728 to handle complex logic. They\u2019re more powerful than <a href=\"https:\/\/coderpad.io\/blog\/development\/python-list-comprehension-guide\/\">list comprehensions<\/a> and more exciting than any new <a href=\"https:\/\/peps.python.org\/pep-0008\/\" target=\"_blank\" rel=\"noopener\">PEP8<\/a> linter.<\/p>\n\n\n<p>Today, we\u2019ll be talking about a few things:<\/p>\n\n\n<ul class=\"wp-block-list\"><li>What magic methods are<\/li><li>Some simple introductory magic method usage<\/li><li>How to programmatically manage class properties<\/li><li>How to overwrite operator symbol functionality<\/li><li>How to make your classes iterable<\/li><\/ul>\n\n\n<p>We also have a cheat sheet for utilizing these magic methods quicker within your projects:<\/p>\n\n<aside class=\"\n    cta-banner\n        \"\ndata-block-name=\"cta-banner\">\n    <div class=\"inner\">\n        <div class=\"content\">\n                            <h2 class=\"headline\">Download Our Magic Methods Cheat Sheet<\/h2>\n            \n                            <div class=\"cta-buttons\">\n                                    <a href=\"\/python-magic-methods-cheat-sheet\/\" class=\"button  js-cta--download\"  data-ga-category=\"CTA\" data-ga-label=\"Download Our Magic Methods Cheat Sheet|Download\">Download<\/a>\n                                <\/div>\n                    <\/div>\n            <\/div>\n<\/aside>\n\n\n<p>Without further ado, let\u2019s dive in!<\/p>\n\n<h2 class=\"wp-block-heading\">What are magic methods?<\/h2>\n\n<p>Magic methods are methods that Python calls on your behalf in specific circumstances. These methods are named in a particular way to quickly distinguish them from other Python methods: they\u2019re preceded and followed by two underscores.<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Speaker<\/span>:<\/span>\n    <span class=\"hljs-comment\"># This is a magic method<\/span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        print(<span class=\"hljs-string\">\"Hello, world!\"<\/span>)\n<span class=\"hljs-comment\"># This will call __init__ and print \"Hello, world!\"<\/span>\ninstance = Speaker()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<blockquote class=\"wp-block-quote\"><p>This is why magic methods also called \u201cdunder methods,\u201d which is a shorthand for \u201cDouble underscore methods.\u201d<\/p><\/blockquote>\n\n<p>In the above code you can see what I\u2019m talking about: Python calls the <code>__init__<\/code> dunder method on your behalf when a new class instance is created.<\/p>\n\n<p>This barely scratches the surface when it comes to the power that magic methods provide. Let\u2019s dive into their usage.<\/p>\n\n<aside class=\"\n    cta-banner\n        \"\ndata-block-name=\"cta-banner\">\n    <div class=\"inner\">\n        <div class=\"content\">\n                            <h2 class=\"headline\">Need a better way to interview candidates? Try CoderPad.<\/h2>\n            \n                            <div class=\"cta-buttons\">\n                                    <a href=\"\/demo\/\" class=\"button  js-cta--get-a-demo\"  data-ga-category=\"CTA\" data-ga-label=\"Need a better way to interview candidates? Try CoderPad.|Get a demo\">Get a demo<\/a>\n                                <\/div>\n                    <\/div>\n            <\/div>\n<\/aside>\n\n\n<h2 class=\"wp-block-heading\">Simple magic method usage<\/h2>\n\n<p>If you\u2019ve ever created a class, you\u2019re likely familiar with the following method:<\/p>\n\n<p><code>__init__(self, \u2026args)<\/code> &#8211; <code>ClassName()<\/code><\/p>\n\n<p>It\u2019s probably the best-known magic method, Python\u2019s <strong>init<\/strong> acts as a class constructor. You can use this to pass initial arguments to a Python class.<\/p>\n\n<p>For example, take the following:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Speaker<\/span>:<\/span>\n\u00a0\u00a0\u00a0 message = <span class=\"hljs-string\">\"\"<\/span>\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, val)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 self.message = val\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">sayIt<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 print(self.message)\n\ninstance = Speaker(<span class=\"hljs-string\">\"Hello, world!\"<\/span>)\ninstance.sayIt()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Here, whenever the <code>Speaker<\/code> class is initialized, it will assign <code>self.message<\/code> to the passed value. We\u2019re then able to use a custom \u201csayIt\u201d method that utilizes <code>self.message<\/code>.<\/p>\n\n<h3 class=\"wp-block-heading\">Clean up class instantiation with <code>del<\/code><\/h3>\n\n<p>In addition to a class initializer, there\u2019s also a class deletion handler:<\/p>\n\n<p><code>__del__(self)<\/code> &#8211; <code>del instance<\/code><\/p>\n\n<p>This method will run any time you call <code>del<\/code> on a class instance. This is particularly useful whenever you have an I\/O operation in the constructor in order to cleanup said I\/O operations.<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> os\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 f = open(<span class=\"hljs-string\">\"temp.csv\"<\/span>, <span class=\"hljs-string\">\"w\"<\/span>)\n\u00a0 \u00a0 \u00a0 \u00a0 f.write(<span class=\"hljs-string\">\"data,more data,testing\"<\/span>)\n\u00a0 \u00a0 \u00a0 \u00a0 f.close()\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__del__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 os.remove(<span class=\"hljs-string\">'temp.csv'<\/span>)\n\u00a0 \u00a0 \u00a0 \u00a0 print(<span class=\"hljs-string\">\"Cleanup done!\"<\/span>)\n\nfirstItem = Test()\n\n<span class=\"hljs-keyword\">del<\/span> firstItem<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>This type is cleanup is integral to ensure your applications are deterministic on each run, which in turn increases general application stability. After all, if you leave remnants of your cache, they\u2019re likely to be picked up by subsequent runs and cause havoc with your application logic.<\/p>\n\n<h2 class=\"wp-block-heading\">How to programmatically manage class properties<\/h2>\n\n<p>Stuff like class constructors and cleanup are par for the course when it comes to class management. Ready for the weird stuff?<br><br>What about declaring attributes that don\u2019t exist? <code>__getattr__<\/code> has you covered.<br><br><code>__getattr__(self, key)<\/code> &#8211; <code>instance.property<\/code> (when <code>property<\/code> doesn\u2019t exist)<\/p>\n\n<p>Simply check what the lookup key is (in this case with the <code>__name<\/code> property) and return a value if you want to create a new property programmatically:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n\u00a0 \u00a0 number = <span class=\"hljs-number\">1<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__getattr__<\/span><span class=\"hljs-params\">(self, __name: str)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">if<\/span> __name == <span class=\"hljs-string\">\"string\"<\/span>:\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"Test\"<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">pass<\/span>\n\n\ntest = Test()\nprint(test.number) <span class=\"hljs-comment\"># Will print `1`<\/span>\nprint(test.string) <span class=\"hljs-comment\"># Will print `\"Test\"`<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>There also exists a slightly different <strong>getattribute<\/strong> built-in:<\/p>\n\n<p><code>__getattribute__(self, key)<\/code> &#8211; <code>instance.property<\/code> (regardless of if <code>property<\/code> exists)<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n\u00a0 \u00a0 number = <span class=\"hljs-number\">1<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__getattribute__<\/span><span class=\"hljs-params\">(self, __name: str)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">if<\/span> __name == <span class=\"hljs-string\">\"string\"<\/span>:\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"Test\"<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">pass<\/span>\n\n\ntest = Test()\nprint(test.number) <span class=\"hljs-comment\"># `None`<\/span>\nprint(test.string) <span class=\"hljs-comment\"># `\"Test\"`<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Notice how instead of <code>test.number<\/code> returning the expected <code>1<\/code> value, it returns a <code>None<\/code>.<\/p>\n\n<p>This is because while <code>__getattr__<\/code> will resolve the existing variables and fallback to the special method when nothing is found, <code>__getattribute__<\/code> runs first and doesn\u2019t fall back to existing values in the class instance.<\/p>\n\n<p>In order to have <code>__getattribute__<\/code> to have the same behavior as <code>__getattr__<\/code>, we need to explicitly tell Python not to get stuck in the <code>__getattribute__<\/code> trap we\u2019ve set up.<\/p>\n\n<p>To do this, we can call <code>super().__getattribute__<\/code>:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n\u00a0\u00a0\u00a0 number = <span class=\"hljs-number\">1<\/span>\n\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__getattribute__<\/span><span class=\"hljs-params\">(self, __name: str)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-string\">\"\"\"\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 We need a \"try\/except\" here, otherwise it will fail during\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 lookup of an invalid key\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \"\"\"<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">try<\/span>:\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 existingVal = super().__getattribute__(__name)\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">if<\/span> existingVal:\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">return<\/span> existingVal\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">except<\/span>:\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">if<\/span> __name == <span class=\"hljs-string\">\"string\"<\/span>:\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"Test\"<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">pass<\/span>\n\n\ntest = Test()\nprint(test.number) <span class=\"hljs-comment\"># Will print `1`<\/span>\nprint(test.string) <span class=\"hljs-comment\"># Will print `\"Test\"`<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<h3 class=\"wp-block-heading\">Customize class property dictionary lookup<\/h3>\n\n\n<p>While <code>__getattr__<\/code> and <code>__getattribute__<\/code> both work wonders for adding in keys programmatically, there\u2019s a problem with that method. When using<a href=\"https:\/\/docs.python.org\/3\/library\/functions.html#dir\" target=\"_blank\" rel=\"noopener\">the <code>dir<\/code> built-in method<\/a>, it won\u2019t show the new keys.<\/p>\n\n\n<p>Let\u2019s show you what I\u2019m talking about with a code sample. Take the following:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n\u00a0 \u00a0 number = <span class=\"hljs-number\">1<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__getattr__<\/span><span class=\"hljs-params\">(self, __name: str)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">if<\/span> __name == <span class=\"hljs-string\">\"string\"<\/span>:\n\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"Test\"<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">pass<\/span>\n\n\ntest = Test()\nprint(dir(test))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>This <code>print<\/code> statement will output all of these keys:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">&#91;<span class=\"hljs-string\">'__class__'<\/span>, <span class=\"hljs-string\">'__delattr__'<\/span>, <span class=\"hljs-string\">'__dict__'<\/span>, <span class=\"hljs-string\">'__dir__'<\/span>, <span class=\"hljs-string\">'__doc__'<\/span>, <span class=\"hljs-string\">'__eq__'<\/span>, <span class=\"hljs-string\">'__format__'<\/span>, <span class=\"hljs-string\">'__ge__'<\/span>, <span class=\"hljs-string\">'__getattr__'<\/span>, <span class=\"hljs-string\">'__getattribute__'<\/span>, <span class=\"hljs-string\">'__gt__'<\/span>, <span class=\"hljs-string\">'__hash__'<\/span>, <span class=\"hljs-string\">'__init__'<\/span>, <span class=\"hljs-string\">'__init_subclass__'<\/span>, <span class=\"hljs-string\">'__le__'<\/span>, <span class=\"hljs-string\">'__lt__'<\/span>, <span class=\"hljs-string\">'__module__'<\/span>, <span class=\"hljs-string\">'__ne__'<\/span>, <span class=\"hljs-string\">'__new__'<\/span>, <span class=\"hljs-string\">'__reduce__'<\/span>, <span class=\"hljs-string\">'__reduce_ex__'<\/span>, <span class=\"hljs-string\">'__repr__'<\/span>, <span class=\"hljs-string\">'__setattr__'<\/span>, <span class=\"hljs-string\">'__sizeof__'<\/span>, <span class=\"hljs-string\">'__str__'<\/span>, <span class=\"hljs-string\">'__subclasshook__'<\/span>, <span class=\"hljs-string\">'__weakref__'<\/span>, <span class=\"hljs-string\">'number'<\/span>]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>This list of keys includes other magic methods, which muddies the output a bit for our needs. Let\u2019s filter those out with the following logic:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">simpledir<\/span><span class=\"hljs-params\">(obj)<\/span>:<\/span>\n\u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> &#91;x <span class=\"hljs-keyword\">for<\/span> x <span class=\"hljs-keyword\">in<\/span> dir(obj) <span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">not<\/span> x.startswith(<span class=\"hljs-string\">'__'<\/span>)]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Now, when we run <code>simpledir(test)<\/code>, we only see:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">&#91;<span class=\"hljs-string\">'number'<\/span>]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>But where is our <code>\u2019string\u2019<\/code> field? It doesn\u2019t show up.<\/p>\n\n<p>This is because while we\u2019ve told Python how to look up the overwritten values, we\u2019ve not told Python which keys we\u2019ve added.<\/p>\n\n<p><br>To do this, we can use the <code>__dir__<\/code> magic method.<\/p>\n\n<p><code>__dir__(self)<\/code> &#8211; <code>dir(instance)<\/code><\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n\u00a0\u00a0\u00a0 number = <span class=\"hljs-number\">1<\/span>\n\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__dir__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 originalList = super().__dir__()\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 originalList.append(<span class=\"hljs-string\">\"string\"<\/span>)\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">return<\/span> originalList\n\u00a0\u00a0\u00a0\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__getattr__<\/span><span class=\"hljs-params\">(self, __name: str)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">if<\/span> __name == <span class=\"hljs-string\">\"string\"<\/span>:\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"Test\"<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">pass<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Customizing <code>dir<\/code> behavior like this will now enable us to treat our dynamic properties as if they existed normally. Now all we\u2019re missing is a way to set values to those properties\u2026<\/p>\n\n<h3 class=\"wp-block-heading\">Set programmatically created keys<\/h3>\n\n<p>While we\u2019re now telling Python which keys we\u2019re programmatically creating and how to lookup the value of those keys, we\u2019re not telling Python how to store those values.<\/p>\n\n<p>Take the following code:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n    number = <span class=\"hljs-number\">1<\/span>\n \n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__getattr__<\/span><span class=\"hljs-params\">(self, __name: str)<\/span>:<\/span>\n        print(<span class=\"hljs-string\">\"Test\"<\/span>);\n        <span class=\"hljs-keyword\">if<\/span> __name == <span class=\"hljs-string\">\"string\"<\/span>:\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"Test\"<\/span>\n        <span class=\"hljs-keyword\">pass<\/span>\n \ntest = Test()\n \ntest.string = <span class=\"hljs-string\">\"Value\"<\/span>\nprint(test.string)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Here, we might expect the <code>print(test.string)<\/code> to output &quot;Test&quot; as well as &quot;Value&quot;, since <code>getattr<\/code> should be called. But, if we look at the log, we only see the following:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-string\">\"Value\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>This is because, once we assign <code>test.string<\/code>, it no longer calls <code>getattr<\/code> the way we expect it to.<\/p>\n\n<p>To solve this problem, we need to use the <code>__setattr__<\/code> magic method to \u201clisten\u201d for property assignment.<\/p>\n\n<p><code>__setattr__(self, key, val)<\/code> &#8211; <code>instance.property = newVal<\/code><\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n\u00a0\u00a0\u00a0 updateCount = <span class=\"hljs-number\">0<\/span>\n\u00a0\u00a0\u00a0 valid = <span class=\"hljs-number\">1<\/span>\n\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__setattr__<\/span><span class=\"hljs-params\">(self, key, val)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 super().__setattr__(<span class=\"hljs-string\">\"updateCount\"<\/span>, self.updateCount + <span class=\"hljs-number\">1<\/span>)\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">pass<\/span>\n\n\ntest = Test()\ntest.valid = <span class=\"hljs-number\">12<\/span>\nprint(test.updateCount)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>&gt; Notice our usage of <code>super().__setattr__<\/code>. We need to do this similarly to how we utilized the <code>super()<\/code> method in <code>__getattribute__<\/code>, otherwise <code>self.updateCount += 1<\/code> would trigger an infinite loop of calls to <code>__setattr__<\/code>.<\/p>\n\n<h3 class=\"wp-block-heading\">Clean up programmatic property instanciation<\/h3>\n\n<p>Just as we can hook into the setting and getting behavior of an attribute, we can also hook into the <code>del<\/code> behavior of an attribute using <code>__delattr__<\/code>.<\/p>\n\n<p>For example, what if we wanted to create a class that acted like a dictionary. For each key created in this dictionary we\u2019d want to automatically create a temporary file. Then, on cleanup (using <code>del<\/code>), let\u2019s remove that file with <code>os.remove<\/code>:<\/p>\n\n<p><code>__delattr__(self, key)<\/code> &#8211; <code>del instance.property<\/code><\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> os\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FileDictionary<\/span>:<\/span>\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__setattr__<\/span><span class=\"hljs-params\">(self, filename, val)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 f = open(filename, <span class=\"hljs-string\">\"w\"<\/span>)\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 f.write(val)\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 f.close()\n\u00a0\u00a0\u00a0\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__delattr__<\/span><span class=\"hljs-params\">(self, filename)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 os.remove(filename)\n\nfileDictionary = FileDictionary()\n\nfileDictionary.README = <span class=\"hljs-string\">\"Hello\"<\/span>\n<span class=\"hljs-keyword\">del<\/span> fileDictionary.README<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Remember, if you\u2019re not cleaning up your side effects, it may cause havoc with future usage of your app. This is why it\u2019s so important to add in <code>__delattr__<\/code> when relevant.<\/p>\n\n<h3 class=\"wp-block-heading\">Convert programatic lookups to index properties<\/h3>\n\n<p>In our most recent <code>FileDictionary<\/code> example, we created a class called \u201cFileDictionary\u201d, but then accessed the child values with the dot accessor:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">fileDictionary.README = <span class=\"hljs-string\">\"Hello\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>However, this dot syntax causes some minor headache: it\u2019s not consistent with how you access properties from a dictionary. The reason we\u2019re not using the standard dictionary syntax is because if you do the following:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">fileDictionary&#91;<span class=\"hljs-string\">'README'<\/span>] = <span class=\"hljs-string\">\"Hello\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>We would quickly get an error from Python:<\/p>\n\n<p>&gt; TypeError: &#8216;FileDictionary&#8217; object is not subscriptable<\/p>\n\n<p>To solve this problem, we need to migrate away from <code>__setattr__<\/code>, which only supports dot notation, to <code>__setitem__<\/code>, which only supports the dictionary-style notation.<\/p>\n\n<p><code>__getitem__(self, key)<\/code> &#8211; <code>instance[property]<\/code><\/p>\n\n<p><code>__setitem__(self, key, val)<\/code> &#8211; <code>instance[property] = newVal<\/code><\/p>\n\n<p><code>__delitem__(self, key)<\/code>&nbsp; &#8211; <code>del instance[property]<\/code><\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> os\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">FileDictionary<\/span>:<\/span>\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__setitem__<\/span><span class=\"hljs-params\">(self, filename, val)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 f = open(filename, <span class=\"hljs-string\">\"w\"<\/span>)\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 f.write(val)\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 f.close()\n\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__delitem__<\/span><span class=\"hljs-params\">(self, filename)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 os.remove(filename)\n\nfileDictionary = FileDictionary()\n\nfileDictionary&#91;<span class=\"hljs-string\">'README'<\/span>] = <span class=\"hljs-string\">\"Hello\"<\/span>\n<span class=\"hljs-keyword\">del<\/span> fileDictionary&#91;<span class=\"hljs-string\">'README'<\/span>]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>As a wonderful side effect, you\u2019re now able to add in a file extension to the <code>fileDictionry<\/code>. This is because bracket notation supports non-ASCII symbols while the dot notation does not.<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">fileDictionary&#91;<span class=\"hljs-string\">'README.md'<\/span>] = <span class=\"hljs-string\">\"Hello\"<\/span>\n<span class=\"hljs-keyword\">del<\/span> fileDictionary&#91;<span class=\"hljs-string\">'README.md'<\/span>]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<h2 class=\"wp-block-heading\">How to replace operator symbol functionality with custom logic<\/h2>\n\n<p>There\u2019s nothing more Pythonic than the simplicity of using simple mathematical symbols to represent mathematic actions.<\/p>\n\n<p>After all, what could more clearly represent the sum of two numbers than:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">sum = <span class=\"hljs-number\">2<\/span> + <span class=\"hljs-number\">2<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Meanwhile, if we have a wrapper around a number:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">sum = numInstance.getNumber() + numInstance.getNumber()<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>It gets a bit harder to read through.<\/p>\n\n<p>What if we could utilize those symbols to handle this custom class logic for us?<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">sum = numInstance + numInstance;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Luckily we can!<\/p>\n\n<p><br>For example, here\u2019s how we can make the <code>+<\/code> symbol run custom logic:<\/p>\n\n<p><code>__add__(self, other)<\/code> &#8211; <code>instance + other<\/code><\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span>:<\/span>\n\u00a0 \u00a0 __internal = <span class=\"hljs-number\">0<\/span>\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, val)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 self.__internal = val\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__add__<\/span><span class=\"hljs-params\">(self, other)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> self.__internal + other.__internal\n\u00a0\n\nfirstItem = Test(<span class=\"hljs-number\">12<\/span>)\nsecondItem = Test(<span class=\"hljs-number\">31<\/span>)\n\n<span class=\"hljs-comment\"># This will call \"__add__\" instead of the traditional arithmetic operation<\/span>\nprint(firstItem + secondItem)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>There\u2019s also other math symbols you can overwrite:<\/p>\n\n<p><code>__sub__(self, other)<\/code> &#8211; <code>instance - other<\/code><\/p>\n\n<p><code>__mul__(self, other)<\/code> &#8211; <code>instance * other<\/code><\/p>\n\n<h3 class=\"wp-block-heading\">Manage comparison symbol behavior<\/h3>\n\n<p>Addition, subtraction, and multiplication aren\u2019t the only usages for operator overloading, however. We can also modify the comparison operators in Python to run custom logic.<\/p>\n\n<p>Let\u2019s say we want to check if two strings match, regardless of casing:<\/p>\n\n<p><code>__eq__(self, other)<\/code> &#8211; <code>instance == other<\/code><\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Test<\/span><span class=\"hljs-params\">()<\/span>:<\/span>\n\u00a0 \u00a0 str = <span class=\"hljs-string\">\"\"<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, val)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 self.str = val\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__eq__<\/span><span class=\"hljs-params\">(self, other)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> self.str.lower() == other.str.lower()\n\nfirstItem = Test(<span class=\"hljs-string\">\"AB\"<\/span>)\nsecondItem = Test(<span class=\"hljs-string\">\"ab\"<\/span>)\n\nprint(firstItem == secondItem)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>You can also have different logic for <code>==<\/code> and <code>!=<\/code> using <code>__ne__<\/code>.<\/p>\n\n<p><code>__ne__(self, other)<\/code> &#8211; <code>instance != other<\/code><\/p>\n\n\n<p>However, if you don\u2019t provide a <code>__ne__<\/code>, but<strong>do<\/strong>provide a <code>__eq__<\/code>, Python will simply negate the <code>__eq__<\/code> logic on your behalf when <code>instance != other<\/code> is called.<\/p>\n\n\n<p>There\u2019s also a slew of magic methods for customizing other comparison operators:<\/p>\n\n<p><code>__lt__(self, other)<\/code> &#8211; <code>instance &amp;lt; other<\/code><\/p>\n\n<p><code>__gt__(self, other)<\/code> &#8211; <code>instance &amp;gt; other<\/code><\/p>\n\n<p><code>__le__(self, other)<\/code> &#8211; <code>instance &amp;lt;= other<\/code><\/p>\n\n<p><code>__ge__(self, other)<\/code> &#8211; <code>instance &amp;gt;= other<\/code><\/p>\n\n<h3 class=\"wp-block-heading\">Overwrite a class\u2019s type casting logic<\/h3>\n\n<p>Python, like any other programming language, has the concept of data types. Similarly, you\u2019re able to convert easily from any of those types to another type using built-in methods of type-casting data.<\/p>\n\n<p><br>For example, if you call <code>bool()<\/code> on a string, it will cast the truthy value to a Boolean.<\/p>\n\n<p><br>What if you could customize the behavior of the <code>bool()<\/code> method? You see where we\u2019re going with this\u2026<\/p>\n\n<p><code>__bool__(self)<\/code> &#8211; <code>bool(instance)<\/code><\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-keyword\">from<\/span> os.path <span class=\"hljs-keyword\">import<\/span> exists\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">File<\/span>:<\/span>\n\u00a0\u00a0\u00a0 file_path = <span class=\"hljs-string\">\"\"<\/span>\n\u00a0\u00a0\u00a0\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__init__<\/span><span class=\"hljs-params\">(self, file_path)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 self.file_path = file_path\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\"># This method should return `True` or `False`<\/span>\n\u00a0\u00a0\u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__bool__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 <span class=\"hljs-keyword\">return<\/span> exists(self.file_path)\n\nfile = File(<span class=\"hljs-string\">\"temp.txt\"<\/span>)\n\n<span class=\"hljs-comment\"># Will return True or False depending on if file exists<\/span>\nprint(bool(file))<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>There\u2019s also other type casts logic you can customize:<\/p>\n\n<p><code>__int__(self)<\/code> &#8211; <code>int(instance)<\/code><\/p>\n\n<p><code>__str__(self)<\/code> &#8211; <code>str(instance)<\/code><\/p>\n\n<h2 class=\"wp-block-heading\">How to make your classes iterable<\/h2>\n\n<p>Let\u2019s say that we\u2019ve used a custom class to build a replacement for a List:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ListLike<\/span>:<\/span>\n\u00a0 \u00a0 length = <span class=\"hljs-number\">0<\/span>\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__getitem__<\/span><span class=\"hljs-params\">(self, key)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> self.__getattribute__(str(key))\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__setitem__<\/span><span class=\"hljs-params\">(self, key, val)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 self.__setattr__(str(key), val)\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__delitem__<\/span><span class=\"hljs-params\">(self, key)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 self.__delattr__(key)\n\n\u00a0 \u00a0 <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">append<\/span><span class=\"hljs-params\">(self, val)<\/span>:<\/span>\n\u00a0 \u00a0 \u00a0 \u00a0 self&#91;str(self.length)] = val\n\u00a0 \u00a0 \u00a0 \u00a0 self.length += <span class=\"hljs-number\">1<\/span>\n\nlistLike = ListLike()\nprint(listLike.length) <span class=\"hljs-comment\"># 0<\/span>\nlistLike.append(<span class=\"hljs-string\">\"Hello\"<\/span>)\nlistLike.append(<span class=\"hljs-string\">\"World\"<\/span>)\nprint(listLike.length) <span class=\"hljs-comment\"># 2<\/span>\nprint(listLike&#91;<span class=\"hljs-number\">0<\/span>]) <span class=\"hljs-comment\"># \"Hello\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>This appears to work amazingly at first glance, until you try to do the following:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\">&#91;x <span class=\"hljs-keyword\">for<\/span> x <span class=\"hljs-keyword\">in<\/span> listLike]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<p>Or any other kind of iteration on the ListLike. You\u2019ll get the following confusingly named error:<\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext shcb-wrap-lines\">'ListLike' object has no attribute '2'<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This is because Python doesn\u2019t know <em>how<\/em>to iterate through your class, and therefore attempts to access a property in the class. This is where <code>__iter__<\/code> comes into play: It allows you to return an iterable to utilize anytime Python might request iterating through the class, like in<a href=\"https:\/\/coderpad.io\/blog\/development\/python-list-comprehension-guide\/\">a list comprehension<\/a>.<\/p>\n\n\n<p><code>__iter__(self)<\/code> &#8211; <code>[x for x in instance]<\/code><\/p>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"Python\" data-shcb-language-slug=\"python\"><span><code class=\"hljs language-python shcb-wrap-lines\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ListLike<\/span>:<\/span>\n    length = <span class=\"hljs-number\">0<\/span>\n \n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__getitem__<\/span><span class=\"hljs-params\">(self, key)<\/span>:<\/span>\n        <span class=\"hljs-keyword\">return<\/span> self.__getattribute__(str(key))\n \n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__setitem__<\/span><span class=\"hljs-params\">(self, key, val)<\/span>:<\/span>\n        self.__setattr__(str(key), val)\n \n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__delitem__<\/span><span class=\"hljs-params\">(self, key)<\/span>:<\/span>\n        self.__delattr__(key)\n \n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">__iter__<\/span><span class=\"hljs-params\">(self)<\/span>:<\/span>\n        isMethod = <span class=\"hljs-keyword\">lambda<\/span> x: type(x).__name__ == <span class=\"hljs-string\">'method'<\/span>\n        <span class=\"hljs-comment\"># Only return non-method keys that are not \"length\"<\/span>\n        <span class=\"hljs-keyword\">return<\/span> iter(&#91;x <span class=\"hljs-keyword\">for<\/span> x <span class=\"hljs-keyword\">in<\/span> dir(self) <span class=\"hljs-keyword\">if<\/span> <span class=\"hljs-keyword\">not<\/span> x.startswith(<span class=\"hljs-string\">'__'<\/span>) <span class=\"hljs-keyword\">and<\/span> x != <span class=\"hljs-string\">'length'<\/span> <span class=\"hljs-keyword\">and<\/span> <span class=\"hljs-keyword\">not<\/span> isMethod(self&#91;x])])\n \n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">def<\/span> <span class=\"hljs-title\">append<\/span><span class=\"hljs-params\">(self, val)<\/span>:<\/span>\n        self&#91;str(self.length)] = val\n        self.length += <span class=\"hljs-number\">1<\/span>\n \nlistLike = ListLike()\n \nlistLike.append(<span class=\"hljs-string\">\"Hello\"<\/span>)\nlistLike.append(<span class=\"hljs-string\">\"World\"<\/span>)\n \n&#91;print(x) <span class=\"hljs-keyword\">for<\/span> x <span class=\"hljs-keyword\">in<\/span> listLike]<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Python<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">python<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<blockquote class=\"wp-block-quote\"><p>Notice that we\u2019re having to return a real list wrapped in the <code>iter<\/code> method for the <code>__iter__<\/code> return value: This is required by Python.&nbsp;<\/p><p>If you don&#8217;t do this, you&#8217;ll get the error:<\/p><p><code>iter() returned non-iterator of type 'list'<\/code><\/p><\/blockquote>\n\n<h3 class=\"wp-block-heading\">Check if an item exists using the \u201cin\u201d keyword<\/h3>\n\n<p>The <code>__iter__<\/code> magic method isn\u2019t the only way to customize traditionally list-like behavior for a class. You can also use the <code>__contains__<\/code> method to add support for simple \u201cis this in the class\u201d checks.<\/p>\n\n<p><code>__contains__(self, item)<\/code> &#8211; <code>key in instance<\/code><\/p>\n\n<p>Something to keep in mind is that if <code>__contains__<\/code> isn&#8217;t defined, Python will use the information provided by <code>__iter__<\/code> to check if the key is present. However, <code>__contains__<\/code> is a more optimized method, since the default <code>__iter__<\/code> checking behavior will iterate through every key until it finds a match.<\/p>\n\n<h2 class=\"wp-block-heading\">Python magic method cheat sheet<\/h2>\n\n<p>Python magic methods can level up your application logic by reducing the amount of boilerplate required to do specific actions, but that\u2019s not its only usecase. Othertimes, you might want to use magic methods to provide an API with a nicer development experience for consuming developers.<\/p>\n\n<p>That said, we know that with so many magic methods it can be difficult to remember them all. This is why we made a cheat sheet that you can download or print out to reference when writing code.&nbsp;<\/p>\n\n<aside class=\"\n    cta-banner\n        \"\ndata-block-name=\"cta-banner\">\n    <div class=\"inner\">\n        <div class=\"content\">\n                            <h2 class=\"headline\">Download Our Magic Methods Cheat Sheet<\/h2>\n            \n                            <div class=\"cta-buttons\">\n                                    <a href=\"\/python-magic-methods-cheat-sheet\/\" class=\"button  js-cta--download\"  data-ga-category=\"CTA\" data-ga-label=\"Download Our Magic Methods Cheat Sheet|Download\">Download<\/a>\n                                <\/div>\n                    <\/div>\n            <\/div>\n<\/aside>\n","protected":false},"excerpt":{"rendered":"<p>Python classes are super powerful. How powerful? They have a feature called &#8220;Magic Methods&#8221; that are, well, magic! They can create properties on-the-fly and so much more.<\/p>\n","protected":false},"author":1,"featured_media":7782,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[9],"tags":[],"persona":[29],"blog-programming-language":[37],"keyword-cluster":[],"class_list":["post-7645","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"_links":{"self":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/7645","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/comments?post=7645"}],"version-history":[{"count":20,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/7645\/revisions"}],"predecessor-version":[{"id":8036,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/posts\/7645\/revisions\/8036"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media\/7782"}],"wp:attachment":[{"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/media?parent=7645"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/categories?post=7645"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/tags?post=7645"},{"taxonomy":"persona","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/persona?post=7645"},{"taxonomy":"blog-programming-language","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/blog-programming-language?post=7645"},{"taxonomy":"keyword-cluster","embeddable":true,"href":"https:\/\/coderpad.io\/wp-json\/wp\/v2\/keyword-cluster?post=7645"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}