Can I add custom methods/attributes to built-in Python types?

Question:

For example—say I want to add a helloWorld() method to Python’s dict type. Can I do this?

JavaScript has a prototype object that behaves this way. Maybe it’s bad design and I should subclass the dict object, but then it only works on the subclasses and I want it to work on any and all future dictionaries.

Here’s how it would go down in JavaScript:

String.prototype.hello = function() {
    alert("Hello, " + this + "!");
}
"Jed".hello() //alerts "Hello, Jed!"

Here’s a useful link with more examples— http://www.javascriptkit.com/javatutors/proto3.shtml

Asked By: jedmao

||

Answers:

Yes, by subclassing those types. See unifying types and classes in Python.

No, this doesn’t mean that actual dicts will have this type, because that would be confusing. Subclassing a builtin type is the preferred way to add functionality.

Answered By: Rafe Kettler

You can’t directly add the method to the original type. However, you can subclass the type then substitute it in the built-in/global namespace, which achieves most of the effect desired. Unfortunately, objects created by literal syntax will continue to be of the vanilla type and won’t have your new methods/attributes.

Here’s what it looks like

# Built-in namespace
import __builtin__

# Extended subclass
class mystr(str):
    def first_last(self):
        if self:
            return self[0] + self[-1]
        else:
            return ''

# Substitute the original str with the subclass on the built-in namespace    
__builtin__.str = mystr

print str(1234).first_last()
print str(0).first_last()
print str('').first_last()
print '0'.first_last()

output = """
14
00

Traceback (most recent call last):
  File "strp.py", line 16, in <module>
    print '0'.first_last()
AttributeError: 'str' object has no attribute 'first_last'
"""
Answered By: TryPyPy

Subclassing is the way to go in Python. Polyglot programmers learn to use the right tool for the right situation – within reason. Something as artfully constructed as Rails (a DSL using Ruby) is painfully difficult to implement in a language with more rigid syntax like Python. People often compare the two saying how similar they are. The comparison is somewhat unfair. Python shines in its own ways. totochto.

Answered By: totochto

Just tried the forbbidenfruit!

here is the code, very simple!

from forbiddenfruit import curse


def list_size(self):
    return len(self)

def string_hello(self):
    print("Hello, {}".format(self))

if __name__ == "__main__":
    curse(list, "size", list_size)
    a = [1, 2, 3]
    print(a.size())
    curse(str, "hello", string_hello)
    "Jesse".hello()
Answered By: dameng
class MyString:
    def __init__(self, string):
        self.string = string
    def bigger_string(self):
        print(' '.join(self.string))

mystring = MyString("this is the string")
mystring.bigger_string()

output

t h i s   i s   t h e   s t r i n g

Dataclass in Python 3.7

from dataclasses import dataclass

@dataclass
class St:

    text : str

    def bigger(self) -> None:
        self.text = list(self.text)
        print(" ".join(self.text))

mys = St("Hello")
mys.bigger()

output

H e l l o
Answered By: PythonProgrammi

NOTE: this QA is marked as duplicate to this one, but IMO it asks for something different. I cannot answer there, so I am answering here.


Specifically, I wanted to inherit from str and add custom attributes. Existing answers (especially the ones saying you can’t) didn’t quite solve it, but this worked for me:

class TaggedString(str):
    """
    A ``str`` with a ``.tags`` set and ``.kwtags`` dict of tags.
    Usage example::
      ts = TaggedString("hello world!", "greeting", "cliche",
                        what_am_i="h4cker")
      (ts.upper(), ts.tags, ts.kwtags)
    """

    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, args[0])

    def __init__(self, s, *tags, **kwtags):
        super().__init__()
        self.tags = set(tags)
        self.kwtags = kwtags

Hopefully this helps someone! Cheers,
Andres

Answered By: fr_andres

Yes indeed, but you have to define a new class of the same type and it should inherit from that type.

For example:

class list(list):
    def __init__(self, *args):
        super().__init__(args)
    def map(self, function):
        return [function(i) for i in self]

a = list(1, 2, 3, 4, 5)

def double(i):
    return i * 2

print(a.map(double))
Answered By: ax39T-Venom

Yes, we can add custom methods and attributes to built-in python types. For example, let us say, you wanna define a new method inside the list class.

Let us think of defining a ‘list’ class and writing your own function like as follows :

class list:
    def custom_method (self):
       return("Hey, I'm a custom method of list class")
#lets create an object here
obj = list([1,2,3])

print(obj.custom_method())
#The above runs fine, but a list has append() method also right?? let's try it

print(obj.append(1))
"""Now you will get Attribute error : list object has no attribute append()"""

Because, when you define class having ‘list’ as class name, you will no longer be able to access the ‘in-built list’ class methods as ‘list’ is treated as a user-defined class rather than a inbuilt class.

So, in order to get rid of this error, you can inherit the properties/members of ‘list’ class and you can define own methods or attributes. So, in this way, you can call user-defined / in-built class methods using the same class name.

Here’s how it looks :

#Extending in-built list class
class list(list):
     def custom_method (self):
         return("Hey, I'm a custom method of list class")
obj = list([1,2,3])
print(obj.custom_method())
obj.append(1)
print(obj)

It runs fine, and outputs modified list as [1,2,3,1].

NOTE : But when you do like this, it may create some ambiguity issues in long run like naming conflicts

For example, if you had a method having same signature that of an inbuilt function in user-defined class(say ‘list’ here), then it will be overridden without your knowledge or notice, thus you may not be able to use its original functionality in future. Considering the above code, if you ever define a method like append(self, value), the original functionality of append() will be lost.

So, it is better to use a different class name for your class name rather than same name as inbuilt class name

For example, you can declare a class like here as follows which does not raise any errors or you will not face any naming conflicts.

class custom_list(list):
     def custom_method (self):
         return("Hey, I'm a custom method of list class")
obj = custom_list([1,2,3])
print(obj.custom_method())
obj.append(1)
print(obj)