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

9

105

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

Atheling answered 15/1, 2011 at 7:27 Comment(4)
You can do this in ruby too by simply opening up the class again, ie: class String; def new_method()...;end; I'm pretty sure python has something like that too.Undertint
@Abdullah I believe you're referring to monkey patching, which is mostly frowned upon.Paviour
Yeah exactly. It's done all over in Rails.Undertint
This is specific to numpy.ndarray but if anyone has come here to try this and failed when trying to add a method to an instance of numpy.ndarray, take a look at the numpy.ndarray.view method.Boling
B
106

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'
"""
Business answered 15/1, 2011 at 7:47 Comment(7)
I guess I'll just have to use the preferred method of subclassing. Maybe I'll grow to love it.Atheling
Note: The __builtin__ module was renamed to builtins in Python3.Ferreira
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.Ingamar
@Ingamar as OP said, "objects created by literal syntax will continue to be of the vanilla type"Freezer
@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.Waves
do you really need to import __builtin__? All of these objects are already available to you by default, hence builtin.Catiline
@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?Slapdash
M
10

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()
Mandie answered 9/5, 2019 at 8:37 Comment(5)
Beyond just being a bad idea in the first place, forbiddenfruit is poorly-implemented and leads to segfaults and memory corruption very easily.Schizo
i like it. i will use.Dangerous
@user2357112supportsMonica Do you have any links where I can read about forbiddenfruit in more detail?Caesaria
I recommend the fishhook module over forbiddenfruit, last time I tried it forbiddenfruit didn't work and fishhook is in active developmentOd
This is built on C API so it won't work on other implementations.Moonshiner
W
9

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))
Watson answered 15/1, 2011 at 7:27 Comment(3)
Thanks. Amazing, powerful... and pretty dangerous! In a multi-module context this class has then to be imported specifically. Just as well, probably.Harelda
this will not work if the list a is constructed differently: a = [1, 2, 3, 4, 5]Ingrid
the __ init__ can be removed (as it's not changed), makes the implementation more elegant in my opinionGrandpa
L
9

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

Lomond answered 20/9, 2019 at 10:55 Comment(0)
C
3

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)
Clothier answered 26/11, 2021 at 14:51 Comment(0)
M
2

To add a custom method and attribute to a list:

import gc

dikt=gc.get_referents(list.__dict__)[0]

dikt['length']= property(lambda self: len(self)) # length attribute 

def push(self,data):self+=[data]

dikt['push']= push  # push method 

a=[1,2,3]
print(a.length)
a.push(4)
print(a.length)
print(a)
Monoceros answered 4/10, 2023 at 9:38 Comment(0)
P
1

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.

Paviour answered 15/1, 2011 at 7:30 Comment(10)
That would be confusing, and very very useful.Mitchum
@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)?Portiaportico
@Portiaportico 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.Mitchum
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.Portiaportico
@Portiaportico #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.Nightdress
@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. :-PPortiaportico
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')Nightdress
@nehemiah and what is the problem with that? You do agree that str is a different type from MyStr, right?Portiaportico
@Portiaportico 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.Nightdress
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.Portiaportico
C
-1

TL;DR
Edit the source code.

Full Explanation
If you know C, you can change the function methods by forking the Python repository and changing the desired file in the Objects folder. Then, read the README.rst file for build instructions.

Cleo answered 29/6, 2023 at 14:45 Comment(0)
L
-2

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.

Lues answered 4/7, 2013 at 22:25 Comment(1)
I don't see a point in subclassing string class. The OP is clear on what he wants.Nightdress

© 2022 - 2024 — McMap. All rights reserved.