
    
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
                
        
        
        
            
{"version":"https:\/\/jsonfeed.org\/version\/1","title":"mathspp.com feed","home_page_url":"https:\/\/mathspp.com\/blog\/tags\/oop","feed_url":"https:\/\/mathspp.com\/blog\/tags\/oop.json","description":"Stay up-to-date with the articles on mathematics and programming that get published to mathspp.com.","author":{"name":"Rodrigo Gir\u00e3o Serr\u00e3o"},"items":[{"title":"Why OOP exists","date_published":"2026-08-26T16:01:00+02:00","id":"https:\/\/mathspp.com\/blog\/why-oop-exists","url":"https:\/\/mathspp.com\/blog\/why-oop-exists","content_html":"<p>Learn the fundamental principles behind OOP and how they connect to the syntax of Python.<\/p>\n\n<h2 id=\"introduction\">Introduction<a href=\"#introduction\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>Welcome!\nThis article will teach you the core ideas behind <strong>object-oriented programming<\/strong>, commonly known as <strong>OOP<\/strong>.\nThis is the article I wish I read many years ago, when I was first learning about OOP in Python.<\/p>\n<p>If you're new to OOP, this article will explain why OOP exists, how it works, and how to work with OOP in Python.\nIf you think you already know OOP, this article will change the way you think about programming and Python.<\/p>\n<h2 id=\"code-is-about-real-world-things\">Code is about real-world things<a href=\"#code-is-about-real-world-things\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>Let me tell you about a programming project I had to do in college where you had to write a library management service.\nWe were expected to work in pairs and I was paired with my good friend Tito.<\/p>\n<p>Tito and I sat down and started going through the problem statement, figuring out what we needed to implement.\nWe got to a point where Tito turned to me and said:<\/p>\n<blockquote>\n<p>&ldquo;It's not obvious to me what's the best way to <strong>represent a <em>book<\/em><\/strong> in our program.\nMaybe you can start implementing the search functionality and I'll think about this for a while.&rdquo;<\/p>\n<\/blockquote>\n<p>The <em>search functionality<\/em> was a set of functions that the problem statement required us to implement:<\/p>\n<ul><li><code>find_by_title(catalog, search_term)<\/code>: returns a sublist with the books whose title contains the given search term<\/li>\n<li><code>find_by_genre(catalog, genre)<\/code>: returns a sublist with the books of the given genre<\/li>\n<li><code>find_by_author(catalog, author)<\/code>: returns a sublist with the books written by the given author<\/li>\n<\/ul><p>I nodded, but then I thought about it for a second.\nIf I don't know <em>anything<\/em> about how to work with books, there's no way I can implement these three functions.\nTito agreed with me and told me he'd provide me with these functions:<\/p>\n<ul><li><code>book_title(book)<\/code>: returns the title of the given book<\/li>\n<li><code>book_genre(book)<\/code>: returns the genre of the given book<\/li>\n<li><code>book_author(book)<\/code>: returns the author of the given book<\/li>\n<\/ul><p>He told me to think of these functions as auxiliary functions that he would implement.\nI didn't have them <em>yet<\/em>, but I could write my search functions <em>trusting<\/em> he'd implement them correctly.<\/p>\n<div class=\"notices blue\">\n<p>In OOP, you have entities with associated data (books with authors, titles, and genres) and a set of functions to operate on those entities (the functions <code>find_by_xxx<\/code>).<\/p>\n<\/div>\n<p>Now, think about it for a second.\nCan <em>you<\/em> implement the functions <code>find_by_title<\/code>, <code>find_by_genre<\/code>, and <code>find_by_author<\/code>, using the auxiliary functions that Tito will implement?\nHow would you go about it?<\/p>\n<p>I worked on it for a bit, and eventually used <a href=\"\/blog\/pydonts\/list-comprehensions-101\">a list comprehension<\/a> to define the function <code>find_by_title<\/code>:<\/p>\n<pre><code class=\"language-python\">def find_by_title(catalog, search_term):\n    search_term = search_term.casefold()\n    return [\n        book\n        for book in catalog\n        if search_term in book_title(book).casefold()\n    ]<\/code><\/pre>\n<p>The function <code>find_by_title<\/code> goes through the list of books called <code>catalog<\/code> with a loop and uses the auxiliary function <code>book_title<\/code> to retrieve the title.\nIt then uses <a href=\"\/blog\/how-to-work-with-case-insensitive-strings\"><code>casefold<\/code> to perform a case-insensitive search<\/a>.<\/p>\n<p>Something worth...<\/p>","summary":"Learn the fundamental principles behind OOP and how that connects to the Python syntax for class definition.","date_modified":"2026-09-29T18:25:53+02:00","tags":["python","programming","oop"],"image":"\/user\/pages\/02.blog\/why-oop-exists\/thumbnail.webp"},{"title":"TIL #127 \u2013 Dynamically changing the type","date_published":"2025-07-20T22:27:00+02:00","id":"https:\/\/mathspp.com\/blog\/til\/dynamically-changing-the-type","url":"https:\/\/mathspp.com\/blog\/til\/dynamically-changing-the-type","content_html":"<p>Today I learned that you can dynamically change the type of an object in Python.<\/p>\n\n<h2 id=\"dynamically-changing-the-type\">Dynamically changing the type<a href=\"#dynamically-changing-the-type\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>An object's type can be checked by using the built-in <code>type<\/code>:<\/p>\n<pre><code class=\"language-py\">print(type(3))  # &lt;class 'int'&gt;<\/code><\/pre>\n<p>You can also access that information directly by checking the dunder attribute <code>__class__<\/code>:<\/p>\n<pre><code class=\"language-py\">print((3).__class__)  # &lt;class 'int'&gt;<\/code><\/pre>\n<p>Apparently, for your own custom types, you can <em>assign<\/em> to the dunder attribute <code>__class__<\/code> and dynamically change the type of an instance.<\/p>\n<p>For example, the variable <code>x<\/code> below holds an instance of the class <code>A<\/code> that doesn't have any methods:<\/p>\n<pre><code class=\"language-py\">class A:\n    def __init__(self, value):\n        self.value = value\n\nx = A(42)<\/code><\/pre>\n<p>Now, you can define the class <code>B<\/code> that defines a method, you can assign <code>x.__class__<\/code> to it, and you can call that method:<\/p>\n<pre><code class=\"language-py\">class B:\n    def mult(self, x):\n        return self.value * x\n\nx.__class__ = B\nprint(type(x))  # &lt;class '__main__.B'&gt; !?\nprint(x.mult(10))  # 420<\/code><\/pre>\n<p>I learned this in a cool lightning talk given at EuroPython 2025!<\/p>","summary":"Today I learned that you can dynamically change the type of an object in Python.","date_modified":"2025-10-20T22:34:56+02:00","tags":["oop","programming","python"],"image":"\/user\/pages\/02.blog\/04.til\/127.dynamically-changing-the-type\/thumbnail.webp"},{"title":"Customising object creation with __new__","date_published":"2024-07-24T16:00:00+02:00","id":"https:\/\/mathspp.com\/blog\/customising-object-creation-with-__new__","url":"https:\/\/mathspp.com\/blog\/customising-object-creation-with-__new__","content_html":"<p>The dunder method <code>__new__<\/code> is used to customise object creation and is a core stepping stone in understanding metaprogramming in Python.<\/p>\n\n<p>The <a href=\"\/blog\/pydonts\/dunder-methods\">dunder method<\/a> <code>__new__<\/code> is a static method that creates new instances of your class and, therefore, can be used to customise that process.<\/p>\n<p>The dunder methods <code>__init__<\/code> and <code>__new__<\/code> can look quite similar at first sight.\n<a href=\"\/blog\/object-initialisation-with-__init__\">The dunder method <code>__init__<\/code><\/a> will <em>initialise<\/em> your instance of your class, but that instance must be created before it can be initialised, and that's the job of the dunder method <code>__new__<\/code>.<\/p>\n<h2 id=\"arguments-of-the-dunder-method-new\">Arguments of the dunder method <code>__new__<\/code><a href=\"#arguments-of-the-dunder-method-new\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The dunder method <code>__new__<\/code> accepts, as arguments, the class we are trying to create an instance of and all of the arguments passed to the class constructor, as shown in the snippet below:<\/p>\n<pre><code class=\"language-py\">class C:\n    def __new__(cls, *args, **kwargs):\n        print(cls, args, kwargs)\n        return super().__new__(cls)\n\nC()  # &lt;class '__main__.C'&gt; () {}\nC(73, True, a=15)  # &lt;class '__main__.C'&gt; (73, True) {'a': 15}<\/code><\/pre>\n<p>The return statement includes <code>super().__new__(cls)<\/code>, which is the typical way in which the object we want to create is brought to life.<\/p>\n<h2 id=\"the-return-value-of-the-dunder-method-new\">The return value of the dunder method <code>__new__<\/code><a href=\"#the-return-value-of-the-dunder-method-new\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>The dunder method <code>__new__<\/code> can return any object whatsoever and the return value of the dunder method <code>__new__<\/code> is the result we get when we instantiate the class.<\/p>\n<p>For example, if we create a class <code>C<\/code> whose dunder method <code>__new__<\/code> returns 73, then whenever we try to instantiate the class <code>C<\/code> we get the number 73:<\/p>\n<pre><code class=\"language-py\">class C:\n    def __new__(cls):\n        return 73\n\nc = C()\nprint(c)  # 73\nprint(type(c))  # int<\/code><\/pre>\n<p>Usually, the dunder method <code>__new__<\/code> will return an instance of the class it's in.\nWhen that is the case, Python will automatically call the dunder method <code>__init__<\/code> on the object that was returned and it will pass along the arguments that were specified in the class constructor.\nSo, if <code>__init__<\/code> gets called, it will receive the same arguments that <code>__new__<\/code> received.<\/p>\n<p>This snippet of code shows that <code>__init__<\/code> only gets called when the return value is an instance of the class where the method <code>__new__<\/code> is defined:<\/p>\n<pre><code class=\"language-py\">class C:\n    def __new__(cls, *, return_73):\n        if return_73:\n            return 73\n        else:\n            return super().__new__(cls)\n\n    def __init__(self, *args, **kwargs):\n        print(\"__init__!\")\n\nx = C(return_73=True)\nprint(x)  # 73\ny = C(return_73=False)  # __init__!\nprint(y)  # &lt;__main__.C object at 0x4ac5a507510&gt;<\/code><\/pre>\n<p>It is relevant to note that Python will call <code>__init__<\/code> on the object returned even if it's an instance of a <em>subclass<\/em> of the class where the method <code>__new__<\/code> is running!<\/p>\n<p>In the snippet below, we create the class <code>C<\/code> and its subclass <code>D<\/code>.\n<code>C<\/code> defines the dunder method <code>__new__<\/code> and both classes define their respective dunder methods <code>__init__<\/code>.\nWhen we create an instance of <code>C<\/code>, we actually get an instance of <code>D<\/code> and the dunder method <code>D.__init__<\/code> gets called automatically:<\/p>\n<pre><code class=\"language-py\">class C:\n    def __new__(cls):\n        return super().__new__(D)\n\n    def __init__(self):\n        print(\"C.__init__\")\n\nclass D(C):\n    def __init__(self):\n        print(\"D.__init__\")\n\nd = C()  # D.__init__\nprint(type(d))  # &lt;class '__main__.D'&gt;<\/code><\/pre>\n<p>This may look a bit weird but it's actually a pattern that is...<\/p>","summary":"The dunder method __new__ is used to customise object creation and is a core stepping stone in understanding metaprogramming in Python.","date_modified":"2025-07-23T16:49:02+02:00","tags":["dunder methods","metaprogramming","oop","programming","python"],"image":"\/user\/pages\/02.blog\/customising-object-creation-with-__new__\/thumbnail.webp"},{"title":"Case-insensitive string class","date_published":"2024-07-24T12:00:00+02:00","id":"https:\/\/mathspp.com\/blog\/case-insensitive-string-class","url":"https:\/\/mathspp.com\/blog\/case-insensitive-string-class","content_html":"<p>This article shows how you can create a case-insensitive string class using some basic meta programming with the dunder method <code>__new__<\/code>.<\/p>\n\n<p>In this article we want to implement a case-insensitive string class, that we will call <code>CIStr<\/code>, such that the comparisons between an instance of <code>CIStr<\/code> and a regular string, or between two <code>CIStr<\/code> instances, are done in a case-insensitive way.<\/p>\n<p>Here are two examples of what we're looking for:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; CIStr(\"Hello\") == \"heLLO\"\nTrue\n&gt;&gt;&gt; \"heLLO\" == CIStr(\"Hello\")\nTrue<\/code><\/pre>\n<h2 id=\"case-insensitive-equality-comparison\">Case-insensitive equality comparison<a href=\"#case-insensitive-equality-comparison\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p><a href=\"\/blog\/how-to-work-with-case-insensitive-strings\">To compare two strings in a case-insensitive way, we need to use the string method <code>casefold<\/code><\/a>.\nSo, when we implement our class <code>CIStr<\/code>, its <code>__eq__<\/code> and <code>__ne__<\/code> methods should use <code>casefold<\/code> on both string values before comparing them:<\/p>\n<pre><code class=\"language-py\">class CIStr(str):\n    def __eq__(self, other):\n        return self.casefold() == other.casefold()\n\n    def __ne__(self, other):\n        return str.__ne__(self.casefold(), other.casefold())\n\nprint(CIStr(\"hello\") == \"HELlo\")  # True<\/code><\/pre>\n<p>This implementation of <code>__eq__<\/code> works because <code>self.casefold()<\/code> will produce a standard string, which is then compared to <code>other.casefold()<\/code> through the default dunder method <code>__eq__<\/code>, not the one we defined here.\nIf we end up changing the <code>CIStr<\/code> string methods to also return case-insensitive strings, then we may need to be more explicit about the fact that we want to use <code>str.__eq__<\/code> after casefolding both strings:<\/p>\n<pre><code class=\"language-py\">class CIStr(str):\n    def __eq__(self, other):\n        return str.__eq__(self.casefold(), other.casefold())\n\nprint(CIStr(\"hello\") == \"HELlo\")  # True<\/code><\/pre>\n<p>If we didn't do this in the final implementation shown here we would hit an infinite recursion loop.<\/p>\n<h2 id=\"all-case-insensitive-comparisons\">All case-insensitive comparisons<a href=\"#all-case-insensitive-comparisons\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>To make sure that all comparisons are case-insensitive, we also need to take care of the comparison operators <code>&lt;<\/code>, <code>&lt;=<\/code>, <code>&gt;<\/code>, and <code>&gt;=<\/code>.\nWe could think of using <code>functools.total_ordering<\/code> but that will not work because <code>str<\/code> already defines the comparison dunder methods and we need to override their default behaviour:<\/p>\n<pre><code class=\"language-py\">class CIStr(str):\n    def __eq__(self, other):\n        return str.__eq__(self.casefold(), other.casefold())\n\n    def __ne__(self, other):\n        return str.__ne__(self.casefold(), other.casefold())\n\n    def __lt__(self, other):  # &lt;\n        return str.__lt__(self.casefold(), other.casefold())\n\n    def __le__(self, other):  # &lt;=\n        return str.__le__(self.casefold(), other.casefold())\n\n    def __gt__(self, other):  # &lt;=\n        return str.__gt__(self.casefold(), other.casefold())\n\n    def __ge__(self, other):  # &lt;=\n        return str.__ge__(self.casefold(), other.casefold())\n\nprint(CIStr(\"hello\") == \"HELlo\")  # True\nprint(CIStr(\"Hello\") &gt; \"abracadabra\")  # True (H comes after A)<\/code><\/pre>\n<h2 id=\"patch-string-methods-to-accept-and-return-case-insensitive-strin\">Patch string methods to accept and return case-insensitive strings<a href=\"#patch-string-methods-to-accept-and-return-case-insensitive-strin\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>Strings have dozens of methods, like <code>upper<\/code>, <code>startswith<\/code>, and <code>split<\/code>.\nWhat if wanted those methods to work with case-insensitive strings <em>and<\/em> return case-insensitive strings automatically?<\/p>\n<p>For example, as it stands, if we use a method like <code>upper<\/code> on a case-insensitive string, we no longer get a case-insensitive string back.\nBut we can fix this.\nWe can patch every single string method so that:<\/p>\n<ol><li>their arguments are always used casefolded; and<\/li>\n<li>their return values are always case-insensitive strings.<\/li>\n<\/ol><p>This can be achieved if we reimplement every single string method by hand but there is a shorter way.\nTo do this, we define two decorators:<\/p>\n<ol><li>a class decorator (<code>patch_string_methods<\/code>) that will wrap <code>CIStr<\/code> which will traverse the string methods, looking for all of the methods that are not dunder...<\/li><\/ol>","summary":"This article shows how you can create a case-insensitive string class using some basic meta programming with the dunder method __new__.","date_modified":"2025-07-23T16:49:02+02:00","tags":["dunder methods","oop","programming","python"],"image":"\/user\/pages\/02.blog\/case-insensitive-string-class\/thumbnail.webp"},{"title":"Describing Descriptors | Pydon&#039;t \ud83d\udc0d","date_published":"2023-06-13T00:00:00+02:00","id":"https:\/\/mathspp.com\/blog\/pydonts\/describing-descriptors","url":"https:\/\/mathspp.com\/blog\/pydonts\/describing-descriptors","content_html":"<p>Descriptors are not black magic and this article will show you that.\nIn fact, you use descriptors every day and you don't even know it.<\/p>\n\n<p>(If you are new here and have no idea what a Pydon't is, you may want to read the\n<a href=\"\/blog\/pydonts\/pydont-manifesto\">Pydon't Manifesto<\/a>.)<\/p>\n<h2 id=\"introduction\">Introduction<a href=\"#introduction\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>Descriptors are a piece of the Python world that many will declare as &ldquo;obscure&rdquo;, &ldquo;dark magic&rdquo;, or &ldquo;too complex to bother learning&rdquo;.\nI will show you that this is <em>not<\/em> true.<\/p>\n<p>While we can all agree that descriptors do not belong in an introductory course to Python, descriptors are far from black magic and with a couple of good examples you can understand how they work and what they are useful for.<\/p>\n<p>In this Pydon't, you will<\/p>\n<ul><li>see what a descriptor is;<\/li>\n<li>learn how to implement your own descriptors;<\/li>\n<li>read about descriptor-related <a href=\"\/blog\/pydonts\/dunder-methods\">dunder methods<\/a>;<\/li>\n<li>learn about the descriptor protocol:\n<ul><li><code>__get__<\/code>;<\/li>\n<li><code>__set__<\/code>; and<\/li>\n<li><code>__delete__<\/code>.<\/li>\n<\/ul><\/li>\n<li>see how to use <code>__set_name__<\/code> with descriptors;<\/li>\n<li>understand that descriptors play a key part in the way Python works, for example in:\n<ul><li>methods;<\/li>\n<li>the built-in <code>property<\/code>;<\/li>\n<li>the built-in <code>staticmethod<\/code>; and<\/li>\n<li>the built-in <code>classmethod<\/code>.<\/li>\n<\/ul><\/li>\n<li>be mind blown by the fact that you use descriptors every day (spoiler: functions <em>are<\/em> descriptors!);<\/li>\n<li>study some common use cases for descriptors; and<\/li>\n<li>solve some exercises and challenges to practise.<\/li>\n<\/ul><!--v--><div class=\"notices blue\">\n<p>You can get all the Pydon'ts as a <a href=\"\/books\/pydonts\">free ebook with over +400 pages and hundreds of tips<\/a>. <a href=\"\/books\/pydonts\">Download the ebook &ldquo;Pydon'ts &ndash; write elegant Python code&rdquo; here<\/a>.<\/p>\n<\/div>\n<!--^-->\n<h2 id=\"the-problem-that-descriptors-solve\">The problem that descriptors solve<a href=\"#the-problem-that-descriptors-solve\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>Consider the implementation of the class <code>Colour<\/code> below, which contains a <code>hex<\/code> attribute for the hexadecimal representation of the colour:<\/p>\n<pre><code class=\"language-py\">class Colour:\n    def __init__(self, hex):\n        self.hex = hex<\/code><\/pre>\n<p>This class doesn't do much (at least, for now...):<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; colour = Colour(\"#f8f8f2\")\n&gt;&gt;&gt; colour.hex\n'#f8f8f2'<\/code><\/pre>\n<p>Now, suppose that you want to add the attributes <code>r<\/code>, <code>g<\/code>, and <code>b<\/code>, respectively for the red, green, and blue, components of the colour.\nThese attributes depend on the value of the hexadecimal representation of the colour, and you <a href=\"\/blog\/pydonts\/properties\">know all about properties<\/a>, so you understand that defining three properties is the way to go, here:<\/p>\n<pre><code class=\"language-py\">class Colour:\n    def __init__(self, hex_string):\n        self.hex = hex_string\n\n    @property\n    def r(self):\n        return int(self.hex[1:3], 16)\n\n    @property\n    def g(self):\n        return int(self.hex[3:5], 16)\n\n    @property\n    def b(self):\n        return int(self.hex[5:7], 16)<\/code><\/pre>\n<p>This works as expected:<\/p>\n<pre><code class=\"language-py\">&gt;&gt;&gt; red = Colour(\"#ff0000\")\n&gt;&gt;&gt; red.r\n255\n&gt;&gt;&gt; red.g, red.b\n(0, 0)<\/code><\/pre>\n<p>If you have an attentive eye, you will notice that the three properties are pretty much the same.\nThe only difference lies in the indices you use when slicing the parameter <code>hex<\/code>.<\/p>\n<p>Descriptors, like properties, let you customise attribute lookup.\nHowever, descriptors give you <em>more<\/em> freedom because you implement a descriptor as a class.\nThis is different from properties that are usually implemented via their getter, setter, and deleter methods.<\/p>\n<p>Later, you will understand that descriptors give you more freedom because properties <em>are<\/em> a specific descriptor.\nIn a way, properties provide a type of descriptor blueprint...<\/p>","summary":"Descriptors are not black magic and this article will show you that.","date_modified":"2025-11-14T12:33:53+01:00","tags":["dunder methods","oop","programming","python"],"image":"\/user\/pages\/02.blog\/02.pydonts\/describing-descriptors\/thumbnail.webp"},{"title":"Properties | Pydon&#039;t \ud83d\udc0d","date_published":"2023-05-14T00:00:00+02:00","id":"https:\/\/mathspp.com\/blog\/pydonts\/properties","url":"https:\/\/mathspp.com\/blog\/pydonts\/properties","content_html":"<p>Learn how to use properties to add dynamic behaviour to your attributes.<\/p>\n\n<p>(If you are new here and have no idea what a Pydon't is, you may want to read the\n<a href=\"\/blog\/pydonts\/pydont-manifesto\">Pydon't Manifesto<\/a>.)<\/p>\n<h2 id=\"introduction\">Introduction<a href=\"#introduction\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>Properties, defined via the <code>property<\/code> built-in, are a Python feature that lets you add dynamic behaviour behind what is typically a static interface: an attribute.\nProperties also have other benefits and use cases, and we will cover them here.<\/p>\n<p>In this Pydon't, you will<\/p>\n<ul><li>understand what a property is;<\/li>\n<li>learn how to implement a property with the built-in <code>property<\/code>;<\/li>\n<li>learn how to use <code>property<\/code> to implement read-only attributes;<\/li>\n<li>see that <code>property<\/code> doesn't have to be used as a decorator;<\/li>\n<li>add setters to your properties;<\/li>\n<li>use setters to do data validation and normalisation;<\/li>\n<li>read about deleters in properties; and<\/li>\n<li>see usages of <code>property<\/code> in the standard library.<\/li>\n<\/ul><!--v--><div class=\"notices blue\">\n<p>You can get all the Pydon'ts as a <a href=\"\/books\/pydonts\">free ebook with over +400 pages and hundreds of tips<\/a>. <a href=\"\/books\/pydonts\">Download the ebook &ldquo;Pydon'ts &ndash; write elegant Python code&rdquo; here<\/a>.<\/p>\n<\/div>\n<!--^-->\n<h2 id=\"what-is-a-property\">What is a property?<a href=\"#what-is-a-property\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>A property is an attribute that is computed dynamically.\nThat's it.\nAnd you will understand what this means in a jiffy!<\/p>\n<h3 id=\"the-problem\">The problem<a href=\"#the-problem\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h3>\n<p>This is a class <code>Person<\/code> with three vanilla attributes:<\/p>\n<pre><code class=\"language-py\">class Person:\n    def __init__(self, first, last):\n        self.first = first\n        self.last = last\n        self.name = f\"{self.first} {self.last}\"\n\njohn = Person(\"John\", \"Doe\")<\/code><\/pre>\n<p>However, there is an issue with the implementation.\nCan you see what is the issue with this implementation?<\/p>\n<p>I'll give you a hint:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; john = Person(\"John\", \"Doe\")\n&gt;&gt;&gt; john.name\n'John Doe'\n&gt;&gt;&gt; john.last = \"Smith\"\n&gt;&gt;&gt; john.name\n## ?<\/code><\/pre>\n<p>When you implement <code>name<\/code> as a regular attribute, it can go out of sync when you change the attributes upon which <code>name<\/code> depended on.<\/p>\n<p>How do we fix this?\nWell, we could provide methods that the user can use to set the first and last names of the <code>Person<\/code> instance, and those methods could keep <code>name<\/code> in sync:<\/p>\n<pre><code class=\"language-py\">class Person:\n    def __init__(self, first, last):\n        self.first = first\n        self.last = last\n        self.name = f\"{self.first} {self.last}\"\n\n    def set_first(self, first):\n        self.first = first\n        self.name = f\"{self.first} {self.last}\"\n\n    def set_last(self, last):\n        self.last = last\n        self.name = f\"{self.first} {self.last}\"\n\njohn = Person(\"John\", \"Doe\")<\/code><\/pre>\n<p>This works:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; john = Person(\"John\", \"Doe\")\n&gt;&gt;&gt; john.name\n'John Doe'\n&gt;&gt;&gt; john.set_first(\"Charles\")\n&gt;&gt;&gt; john.name\n'Charles Doe'<\/code><\/pre>\n<p>However, we had to add two methods that look pretty much the same...\nAnd this would get worse if we introduced an attribute for middle names, for example...\nEssentially, this isn't a very Pythonic solution &ndash; it isn't very elegant.\n(Or, at least, we can do better!)<\/p>\n<p>There is another alternative...\nInstead of updating <code>name<\/code> when the other attributes are changed, we could add a method that computes the name of the user on demand:<\/p>\n<pre><code class=\"language-py\">class Person:\n    def __init__(self, first, last):\n        self.first = first\n        self.last = last\n\n    def get_name(self):\n        return f\"{self.first} {self.last}\"<\/code><\/pre>\n<p>This also works:<\/p>\n<pre><code class=\"language-pycon\">&gt;&gt;&gt; john = Person(\"John\", \"Doe\")\n&gt;&gt;&gt; john.get_name()\n'John Doe'\n&gt;&gt;&gt; john.first = \"Charles\"\n&gt;&gt;&gt; john.get_name()\n'Charles Doe'<\/code><\/pre>\n<p>But this isn't very elegant,...<\/p>","summary":"Learn how to use properties to add dynamic behaviour to your attributes.","date_modified":"2025-11-14T12:33:53+01:00","tags":["oop","programming","python"],"image":"\/user\/pages\/02.blog\/02.pydonts\/properties\/thumbnail.webp"},{"title":"Implementing a case-insensitive dictionary","date_published":"2023-01-29T00:00:00+01:00","id":"https:\/\/mathspp.com\/blog\/case-insensitive-dictionary","url":"https:\/\/mathspp.com\/blog\/case-insensitive-dictionary","content_html":"<p>How do you implement a case-insensitive (or caseless) dictionary? In this article we explore solutions where we inherit from the built-in <code>dict<\/code>, the abstract base class <code>MutableMapping<\/code> from the standard module <code>collections.abc<\/code>, and the <code>UserDict<\/code> in the standard module <code>collections<\/code>.<\/p>\n\n<h2 id=\"introduction\">Introduction<a href=\"#introduction\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>In a recent article, I showed why you should use the string method <code>casefold<\/code> when working with case-insensitive strings.\nIn that article, I also presented <a href=\"\/blog\/how-to-work-with-case-insensitive-strings#how-to-implement-a-case-insensitive-dictionary-in-python\">a basic implementation of a case-insensitive dictionary<\/a>.\nHowever, that implementation (reproduced below) had a couple of issues.<\/p>\n<pre><code class=\"language-py\">class CaseInsensitiveDict(dict):\n    \"\"\"Case-insensitive dictionary implementation.\"\"\"\n\n    def __getitem__(self, key):\n        return dict.__getitem__(self, key.casefold())\n\n    def __setitem__(self, key, value):\n        return dict.__setitem__(self, key.casefold(), value)\n\n    def __delitem__(self, key):\n        return dict.__delitem__(self, key.casefold())<\/code><\/pre>\n<p>Our class <code>CaseInsensitiveDict<\/code> implements the three <a href=\"\/blog\/pydonts\/dunder-methods\">dunder methods<\/a> <code>__getitem__<\/code>, <code>__setitem__<\/code>, and <code>__delitem__<\/code>, and that lets us mimic the main behaviour of <code>dict<\/code>.\nHowever, this implementation is incomplete.<\/p>\n<p>We can create an instance of a case-insensitive dictionary and expose the issues with the implementation above:<\/p>\n<pre><code class=\"language-py\">&gt;&gt;&gt; d = CaseInsensitiveDict()\n&gt;&gt;&gt; d[\"mathspp\"] = \"https:\/\/mathspp.com\/\"\n&gt;&gt;&gt; d.get(\"MATHSPP\", \"not found\")\n'not found'<\/code><\/pre>\n<p>The issues that we will find all revolve around what the example above shows.\nAlthough we implemented the three main dunder methods that let you add \/ access \/ delete keys from a dictionary, the other dictionary methods will not use the dunder methods to perform their operations.<\/p>\n<h2 id=\"case-insensitive-dictionary-inheriting-from-dict\">Case-insensitive dictionary inheriting from <code>dict<\/code><a href=\"#case-insensitive-dictionary-inheriting-from-dict\" class=\"toc-anchor after\" data-anchor-icon=\"#\" aria-label=\"Anchor\"><\/a><\/h2>\n<p>If we wanted to implement a case-insensitive dictionary that inherits from <code>dict<\/code> directly, we would have to provide implementations of most of the <code>dict<\/code> methods in terms of the three dunder methods that we already have.<\/p>\n<p>If you are up for the challenge, open your Python REPL and inspect the result of <code>dir(dict)<\/code>.\nThat list will contain all the (dunder) methods that <code>dict<\/code> knows about.\nGo over that list and implement all the methods that need to be written in terms of our case-insensitive operations.<\/p>\n<p>Such an implementation is included below for reference:<\/p>\n<pre><code class=\"language-py\">_no_default = object()\n\nclass CaseInsensitiveDict(dict):\n    \"\"\"Case-insensitive dictionary implementation.\"\"\"\n\n    def __getitem__(self, key):\n        return dict.__getitem__(self, key.casefold())\n\n    def __setitem__(self, key, value):\n        return dict.__setitem__(self, key.casefold(), value)\n\n    def __delitem__(self, key):\n        return dict.__delitem__(self, key.casefold())\n\n    # ---\n\n    def __contains__(self, key):\n        return dict.__contains__(self, key.casefold())\n\n    def __init__(self, seed=None, **kwargs):\n        super().__init__()\n        # Defer work to the method .update.\n        self.update(seed)\n        self.update(kwargs)\n\n    def __or__(self, other):\n        \"\"\"Dictionary updating with the pipe operator |.\"\"\"\n        base = self.copy()\n        base.update(other)\n        return base\n\n    def __ror__(self, other):\n        \"\"\"Dictionary updating with | and a regular dict on the left.\"\"\"\n        base = CaseInsensitiveDict(other)\n        base.update(other)\n        return base\n\n    def copy(self):\n        return CaseInsensitiveDict(dict.copy(self))\n\n    def get(self, key, default=None):\n        return dict.get(self, key.casefold(), default)\n\n    def pop(self, key, default=_no_default):\n        if default is _no_default:\n            return dict.pop(self, key.casefold())\n        else:\n            return dict.pop(self, key.casefold(), default)\n\n    def setdefault(self, key, default=None):\n        return dict.setdefault(self, key.casefold(), default)\n\n    def update(self, seed=None, **kwargs):\n        if seed is None:\n            seed = {}\n\n        # Is the seed a mapping...\n        if hasattr(seed, \"items\"):\n            for key, value in seed.items():\n                self[key] = value\n        # or an iterable?\n        else:\n            for key, value in seed:\n                self[key] = value\n\n        for key, value in kwargs.items():\n            self[key] = value<\/code><\/pre>\n<hr><p>Stay tuned for the remainder of the article!...<\/p>","summary":"How do you implement a case-insensitive (or caseless) dictionary? In this article we explore solutions where we inherit from the built-in dict, the abstract base class MutableMapping from the standard module collections.abc, and the UserDict in the standard module collections.","date_modified":"2025-07-23T16:49:02+02:00","tags":["dunder methods","oop","programming","python"],"image":"\/user\/pages\/02.blog\/case-insensitive-dictionary\/thumbnail.webp"}]}
