{"id":113,"date":"2025-05-23T05:21:00","date_gmt":"2025-05-23T05:21:00","guid":{"rendered":"https:\/\/www.alerainfotech.com\/?p=113"},"modified":"2025-05-23T05:21:00","modified_gmt":"2025-05-23T05:21:00","slug":"mastering-__str__-and-method-types-in-python-how-built-ins-work-and-when-to-use-what","status":"publish","type":"post","link":"https:\/\/www.alerainfotech.com\/home\/2025\/05\/23\/mastering-__str__-and-method-types-in-python-how-built-ins-work-and-when-to-use-what\/","title":{"rendered":"Mastering\u00a0__str__()\u00a0and Method Types in Python: How Built-ins Work and When to Use What"},"content":{"rendered":"\n<p>When you start diving deeper into Python classes, you\u2019ll likely come across strange-looking method names like&nbsp;<code>__str__<\/code>,&nbsp;<code>__repr__<\/code>, or&nbsp;<code>__len__<\/code>. These are&nbsp;<strong>dunder methods<\/strong>&nbsp;(short for &#8220;double underscore&#8221;), and they power many of Python\u2019s built-in behaviors.<\/p>\n\n\n\n<p>But they also raise some excellent questions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Why do we use\u00a0<code>__str__()<\/code>\u00a0and not just a method called\u00a0<code>str()<\/code>?<\/li>\n\n\n\n<li>Why does\u00a0<code>str(obj)<\/code>\u00a0work when we never directly call a method on the object?<\/li>\n\n\n\n<li>Can we do the same with any method?<\/li>\n\n\n\n<li>What\u2019s the difference between class methods and static methods?<\/li>\n<\/ul>\n\n\n\n<p>This article brings clarity to all of these with clean examples and explanations.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1. Why Python Uses&nbsp;<code>__str__()<\/code>&nbsp;Instead of Just&nbsp;<code>str()<\/code><\/strong><\/h2>\n\n\n\n<p>The&nbsp;<code>__str__()<\/code>&nbsp;method is part of Python\u2019s&nbsp;<strong>data model<\/strong>&nbsp;\u2014 a special system that lets you control how your objects interact with the language itself.<\/p>\n\n\n\n<p>If you want to control how your object looks when passed to&nbsp;<code>print()<\/code>&nbsp;or&nbsp;<code>str()<\/code>, override&nbsp;<code>__str__()<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Example:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">pythonCopyEdit<code>class Book:\n    def __init__(self, title):\n        self.title = title\n\n    def __str__(self):\n        return f\"Book: {self.title}\"\n\nbook = Book(\"Python 101\")\nprint(str(book))  # Output: Book: Python 101\n<\/code><\/pre>\n\n\n\n<p>Here,&nbsp;<code>str(book)<\/code>&nbsp;calls&nbsp;<code>book.__str__()<\/code>&nbsp;internally. But what happens if you try using just&nbsp;<code>str()<\/code>&nbsp;as a method?<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2. What If You Define a&nbsp;<code>str()<\/code>&nbsp;Method?<\/strong><\/h2>\n\n\n\n<p>You&nbsp;<strong>can<\/strong>&nbsp;define a method named&nbsp;<code>str()<\/code>&nbsp;\u2014 but Python won\u2019t use it for&nbsp;<code>str(obj)<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>class Book:<br>    def str(self):<br>        return \"This is just a regular method\"<br><br>book = Book()<br>print(book.str())     # \u2705 OK: manual method call<br>print(str(book))      # \u274c NOT OK: Python ignores your `str()` method<br><\/code><\/pre>\n\n\n\n<p><strong>Why?<\/strong><br>Because Python uses&nbsp;<code>__str__()<\/code>&nbsp;under the hood \u2014 it doesn\u2019t look for a method literally named&nbsp;<code>str<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3. How&nbsp;<code>str(obj)<\/code>&nbsp;Really Works<\/strong><\/h2>\n\n\n\n<p><code>str(obj)<\/code>&nbsp;is a&nbsp;<strong>built-in function<\/strong>. Internally, Python translates it like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>def str(obj):<br>    if hasattr(obj, '__str__'):<br>        return obj.__str__()<br>    else:<br>        return obj.__repr__()<br><\/code><\/pre>\n\n\n\n<p>That\u2019s why you can do:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>str(obj)  # works, calls obj.__str__()<br><\/code><\/pre>\n\n\n\n<p>But not:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>obj.str()  # doesn't connect to str(obj) at all<br><\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4. Other Built-in Functions and Their Dunder Methods<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Built-in Function<\/th><th>Internally Calls<\/th><\/tr><\/thead><tbody><tr><td><code>str(obj)<\/code><\/td><td><code>obj.__str__()<\/code><\/td><\/tr><tr><td><code>len(obj)<\/code><\/td><td><code>obj.__len__()<\/code><\/td><\/tr><tr><td><code>repr(obj)<\/code><\/td><td><code>obj.__repr__()<\/code><\/td><\/tr><tr><td><code>abs(obj)<\/code><\/td><td><code>obj.__abs__()<\/code><\/td><\/tr><tr><td><code>iter(obj)<\/code><\/td><td><code>obj.__iter__()<\/code><\/td><\/tr><tr><td><code>next(obj)<\/code><\/td><td><code>obj.__next__()<\/code><\/td><\/tr><tr><td><code>a + b<\/code><\/td><td><code>a.__add__(b)<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>So Python has a special internal system to connect these&nbsp;<strong>built-in functions<\/strong>&nbsp;to your&nbsp;<strong>custom methods<\/strong>, as long as they follow the&nbsp;<code>__dunder__<\/code>&nbsp;format.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5. Can You Call Any Method Without an Object?<\/strong><\/h2>\n\n\n\n<p>Only&nbsp;<strong>built-in functions like&nbsp;<code>str()<\/code>&nbsp;or&nbsp;<code>len()<\/code><\/strong>&nbsp;can be called on your object like that \u2014 because Python is hardwired to look for corresponding&nbsp;<code>__dunder__<\/code>&nbsp;methods.<\/p>\n\n\n\n<p>For regular methods, you&nbsp;<strong>must use an object<\/strong>:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>class Car:<br>    def drive(self):<br>        print(\"Driving\")<br><br>Car.drive()    # \u274c Error: missing required argument 'self'<br>Car().drive()  # \u2705 Works<br><\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6. Types of Methods in Python: Instance, Class, Static<\/strong><\/h2>\n\n\n\n<p>When defining methods inside a class, you have three choices \u2014 and each behaves differently when called.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Instance Methods<\/strong>&nbsp;(Default)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Take\u00a0<code>self<\/code>\u00a0as the first parameter<\/li>\n\n\n\n<li>Require an instance to call<\/li>\n\n\n\n<li>Can access both instance and class variables<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>class Dog:<br>    def bark(self):<br>        print(\"Woof!\")<br><br>dog = Dog()<br>dog.bark()  # \u2705<br><\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Class Methods<\/strong>&nbsp;(<code>@classmethod<\/code>)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Take\u00a0<code>cls<\/code>\u00a0(the class) as the first parameter<\/li>\n\n\n\n<li>Can be called on the class<\/li>\n\n\n\n<li>Useful for factory methods or class-level logic<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>class Dog:<br>    breed = \"Labrador\"<br><br>    @classmethod<br>    def get_breed(cls):<br>        return cls.breed<br><br>print(Dog.get_breed())  # \u2705 Labrador<br><\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Static Methods<\/strong>&nbsp;(<code>@staticmethod<\/code>)<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>No automatic first argument (<code>self<\/code>\u00a0or\u00a0<code>cls<\/code>)<\/li>\n\n\n\n<li>Can be called on class or instance<\/li>\n\n\n\n<li>Acts like a regular function inside the class<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>class Math:<br>    @staticmethod<br>    def add(a, b):<br>        return a + b<br><br>print(Math.add(2, 3))  # \u2705 5<br><\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>7. Quick Comparison Table<\/strong><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Feature<\/th><th>Instance Method<\/th><th>Class Method<\/th><th>Static Method<\/th><\/tr><\/thead><tbody><tr><td>First arg<\/td><td><code>self<\/code><\/td><td><code>cls<\/code><\/td><td>None<\/td><\/tr><tr><td>Access instance vars<\/td><td>Yes<\/td><td>No<\/td><td>No<\/td><\/tr><tr><td>Access class vars<\/td><td>Yes<\/td><td>Yes<\/td><td>No<\/td><\/tr><tr><td>Needs instance?<\/td><td>Yes<\/td><td>No<\/td><td>No<\/td><\/tr><tr><td>Use case<\/td><td>Object behavior<\/td><td>Factories, class config<\/td><td>Utility functions<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>8. Real-World Example Using All Three<\/strong><\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>class Circle:<br>    pi = 3.14159<br><br>    def __init__(self, radius):<br>        self.radius = radius<br><br>    def area(self):<br>        return Circle.pi * self.radius * self.radius<br><br>    @classmethod<br>    def from_diameter(cls, diameter):<br>        return cls(diameter \/ 2)<br><br>    @staticmethod<br>    def describe():<br>        return \"Circles are round.\"<br><\/code><\/pre>\n\n\n\n<p>Usage:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>c = Circle.from_diameter(10)  # uses class method<br>print(c.area())               # instance method<br>print(Circle.describe())      # static method<br><\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Python uses\u00a0<code>__str__()<\/code>\u00a0instead of\u00a0<code>str()<\/code>\u00a0to\u00a0<strong>avoid name conflicts<\/strong>\u00a0and signal internal behavior.<\/li>\n\n\n\n<li><code>str(obj)<\/code>\u00a0works because it\u2019s a built-in function that looks for\u00a0<code>__str__()<\/code>\u00a0behind the scenes.<\/li>\n\n\n\n<li>Only specific built-in functions like\u00a0<code>str()<\/code>,\u00a0<code>len()<\/code>, and\u00a0<code>abs()<\/code>\u00a0do this \u2014 your own methods require normal calls.<\/li>\n\n\n\n<li>Class methods (<code>@classmethod<\/code>) are used for class-wide behavior, while static methods (<code>@staticmethod<\/code>) are just utility helpers.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Mastering these distinctions not only makes your code cleaner but helps you build classes that behave just like Python\u2019s built-in types.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When you start diving deeper into Python classes, you\u2019ll likely come across strange-looking method names like&nbsp;__str__,&nbsp;__repr__, or&nbsp;__len__. These are&nbsp;dunder methods&nbsp;(short for &#8220;double underscore&#8221;), and they power many of Python\u2019s built-in behaviors. But they also raise some excellent questions: This article brings clarity to all of these with clean examples and explanations. 1. Why Python Uses&nbsp;__str__()&nbsp;Instead [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-113","post","type-post","status-publish","format-standard","hentry","category-python-blog"],"_links":{"self":[{"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/posts\/113","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/comments?post=113"}],"version-history":[{"count":1,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/posts\/113\/revisions"}],"predecessor-version":[{"id":114,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/posts\/113\/revisions\/114"}],"wp:attachment":[{"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/media?parent=113"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/categories?post=113"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.alerainfotech.com\/home\/wp-json\/wp\/v2\/tags?post=113"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}