40

How can I extend a builtin class in python? I would like to add a method to the str class.
I've done some searching but all I'm finding is older posts, I'm hoping someone knows of something newer.

3

4 Answers 4

44

Just subclass the type

>>> class X(str):
...     def my_method(self):
...         return int(self)
...
>>> s = X("Hi Mom")
>>> s.lower()
'hi mom'
>>> s.my_method()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in my_method
ValueError: invalid literal for int() with base 10: 'Hi Mom'

>>> z = X("271828")
>>> z.lower()
'271828'
>>> z.my_method()
271828
Sign up to request clarification or add additional context in comments.

6 Comments

I was hoping that I wouldn't have to subclass but after some more research I have come to the conclusion that it is not possible any other way, so I'll accept this answer.
@Unkwntech: Can't imagine a better way than subclassing. Seriously. Whatever you had in mind (monkeypatching?) never seems to work out as well as a clear, precise, obvious subclass.
@S.Lott - Ruby's open classes and C#'s extension methods come to mind.
@orip: Aware of these. Find monkeypatching in all forms to be a management nightmare -- they effectively prevent reuse by injecting features in obscure ways.
@dagfr by overriding the .lower() method in the X class and returning a new X object
|
18

One way could be to use the "class reopening" concept (natively existing in Ruby) that can be implemented in Python using a class decorator. An exemple is given in this page: http://www.ianbicking.org/blog/2007/08/opening-python-classes.html

I quote:

I think with class decorators you could do this:

@extend(SomeClassThatAlreadyExists)
class SomeClassThatAlreadyExists:
    def some_method(self, blahblahblah):
        stuff

Implemented like this:

def extend(class_to_extend):
    def decorator(extending_class):
        class_to_extend.__dict__.update(extending_class.__dict__)
        return class_to_extend
    return decorator

1 Comment

Python 3.9, __dict__ is no longer a dict but a mappingproxy, you have to use @MVP's solution instead.
7

Assuming that you can not change builtin classes. To simulate a "class reopening" like Ruby in Python3 where __dict__ is an mappingproxy object and not dict object :

def open(cls):
  def update(extension):
    for k,v in extension.__dict__.items():
      if k != '__dict__':
        setattr(cls,k,v)
    return cls
  return update


class A(object):
  def hello(self):
    print('Hello!')

A().hello()   #=> Hello!

#reopen class A
@open(A)
class A(object):
  def hello(self):
    print('New hello!')
  def bye(self):
    print('Bye bye')


A().hello()   #=> New hello!
A().bye()     #=> Bye bye

In Python2 I could also write a decorator function 'open' as well:

def open(cls):
  def update(extension):
    namespace = dict(cls.__dict__)
    namespace.update(dict(extension.__dict__))
    return type(cls.__name__,cls.__bases__,namespace)
  return update

1 Comment

At last I can do this like in Ruby. Works like a charm. I prefer the second definition.
5

I know this question is quite old now, but just in case someone comes here later, there's a library that allows actually extending built-ins types.

Available for Python 3.7 - 3.11.

ExType

Disclaimer: I'm one of the authors

1 Comment

Excellent project, going into my bookmarks as I see myself using it in the future

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.