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

37,495

Solution 1

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'
"""

Solution 2

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()

Solution 3

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))

Solution 4

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

Solution 5

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)
Share:
37,495
Sam
Author by

Sam

Senior JavaScript Engineer JavaScript, TypeScript, HTML, CSS, Node, Gulp, C#, Animation

Updated on November 27, 2021

Comments

  • Sam
    Sam over 2 years

    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

  • Sam
    Sam over 13 years
    I guess I'll just have to use the preferred method of subclassing. Maybe I'll grow to love it.
  • changokun
    changokun over 10 years
    Note: The __builtin__ module was renamed to builtins in Python3.
  • Evgeni Sergeev
    Evgeni Sergeev about 10 years
    That would be confusing, and very very useful.
  • Veky
    Veky over 8 years
    @EvgeniSergeev Why do you think it would be very very useful? I have never really understood those people who wish for this. Do you think 5 should not be of type int, or do you think type int should be mutable (while 5 itself is immutable)?
  • Evgeni Sergeev
    Evgeni Sergeev over 8 years
    @Veky I want any_dictionary_instance.len() or .size(), in addition to the available any_dictionary_instance.__len__() and len(any_dictionary_instance) that calls it. Size is semantically a property of containers, so it's more convenient to write as you say e.g. "iterate from 0 to the object's size" ... oops, can't express that, the language forces me to rephrase it as "iterate from 0 to the size of object". For nothing better than historical reasons, I gather.
  • Veky
    Veky over 8 years
    Yes, but not every container has size (as a property), Saxon Genitive is not really a supported grammatical construction in many programming languages :-), and most importantly, Python has a much better way of iterating through containers, that doesn't expose size. But ok, I think I can relate to grammatical wishes, if "object's size" is really a more natural phrase for you than "size of object". I'm not a native speaker of English, and to me they both sound equally ok.
  • Nadu
    Nadu over 7 years
    Well I encountered a problem in python 2. After that I can't check for the type anymore >type('')==str< just gives >false<. When I type("") I get <type 'str'>, but somehow the compare doesn't work anymore. Before running the 'line builtin.str = mystr' the check for type works.
  • Alex Hall
    Alex Hall over 7 years
    @Nadu as OP said, "objects created by literal syntax will continue to be of the vanilla type"
  • Chris Cogdon
    Chris Cogdon almost 7 years
    @nadu. Typically you shouldn't be using == when checking for types. Instead use isinstance("", str), which will return True if the first value is an instance of the second, or any subclass of the second.
  • nehem
    nehem almost 6 years
    @Veky #Facepalm, this is what happens when you forget to see the spirit of an argument instead go on rambling. What Evgeni meant was Python as a flexible dynamic typed language, should allow us to add custom methods to the built-in classes. After all it's his code and his runtime.
  • Veky
    Veky almost 6 years
    @nehemiah and you know what Evgeni meant... how exactly? I read his words. Did you read his comment above? He explicitly lamented the fact that he can't use Saxon genitive when accessing the size. BTW yes, it's his code, until someone else gets to maintain it. And he can obviously change and recompile the runtime environment as he pleases. :-P
  • nehem
    nehem almost 6 years
    I'm not here to go overboard again to add the benefits of such features, but trust me there are countless times where I thought of adding my own methods to the built-in string class. Python simply doesn't allow so. Yes I can subclass, it's daft to call that as a solution, because the moment you subclass every time you have to do this x = MyStr('hello')
  • nehem
    nehem almost 6 years
    I don't see a point in subclassing string class. The OP is clear on what he wants.
  • Veky
    Veky over 5 years
    @nehemiah and what is the problem with that? You do agree that str is a different type from MyStr, right?
  • nehem
    nehem over 5 years
    @Veky MyStr is not str That is the problem. The OP wants to add methods into built-in type just like Ruby or JavaScript allows. Hope there will be a PEP for this and be addressed in future versions.
  • Veky
    Veky over 5 years
    I hope there will not be (though, now that Guido has stepped down, everything is possible:-/). The whole point of knowing Python types is knowing what methods they have. If anyone could add random functionality to str, then you just don't know what type 'abc' is. That is unacceptable, of course. And yes, JS does have big problems with it: witness motools Array.flatten fiasco.
  • user2357112
    user2357112 almost 5 years
    Beyond just being a bad idea in the first place, forbiddenfruit is poorly-implemented and leads to segfaults and memory corruption very easily.
  • Vitaly Fadeev
    Vitaly Fadeev over 4 years
    i like it. i will use.
  • Peter Schorn
    Peter Schorn over 4 years
    do you really need to import __builtin__? All of these objects are already available to you by default, hence builtin.
  • DarthVlader
    DarthVlader about 4 years
    @user2357112supportsMonica Do you have any links where I can read about forbiddenfruit in more detail?
  • mike rodent
    mike rodent over 3 years
    Thanks. Amazing, powerful... and pretty dangerous! In a multi-module context this class has then to be imported specifically. Just as well, probably.
  • Ryan Laursen
    Ryan Laursen about 3 years
    I recommend the fishhook module over forbiddenfruit, last time I tried it forbiddenfruit didn't work and fishhook is in active development
  • user26742873
    user26742873 about 3 years
    This is built on C API so it won't work on other implementations.
  • astralwolf
    astralwolf almost 3 years
    @AlexHall is there any reason why built-ins created by literal syntax are continued to be constructed by the vanilla dunder? It seems that it is easily possible to make int(3) return a string (or whatever) by overloading __new__, but why doesnt '3' do the same? I mean, it seems to be tehcnically possible?
  • Nik O'Lai
    Nik O'Lai over 2 years
    this will not work if the list a is constructed differently: a = [1, 2, 3, 4, 5]