Add to Class like Trait or Interface

Feature

add built-in new function decorator that help to add more methods to built-in or previous projects build classes

Pitch

I wish you add this decorator to new update to avoid rewrite it every time i start new project and would like to add new methods to existing classes in language,
Maybe you can help to make it more generic like traits that in rust

def add_to_class(Class): 
    def wrapper(obj): 
        setattr(Class, obj.__name__, obj)
     return wrapper

Let’s have a quick look at how to use it. I plan to implement a class A with a method do. Instead of having code for both A and do in the same code block, I can first declare the class A and create an instance a.

class A: 
    def __init__(self): 
        self.b = 1

a = A() 

Next I define the method do as I normally would, but not in class A’s scope. Instead, I decorate this method by add_to_class with class A as its argument. In doing so, the method is able to access the member variables of A as I would expect if it had been defined as part of A’s definition. Let’s see what happens when I invoke it for the instance a.

@add_to_class(A)
def do(self): 
    print('Class attribute "b" is', self.b)

a.do()

thanks alot

1 Like

Why not use inheritance to augment existing classes?

i wish to add any method to built-in class like strings ,integers or whatever without redefine new classes and change overall codes already built

if you try to code using rust it will give you more powerful ways of doing anything without rewrite the overall types or structs built-in language

No thanks. Ruby allowed that, and it’s gotten horrifically out of hand:

Think you know what’s part of the language and what got monkeypatched in? Try distinguishing Ruby (the language) from Rails (popular web app framework). Quiz: Ruby or Rails?

You can always make more functions which take strings as parameters, but they won’t be available on string literals. You can, of course, create your own subclass of str which has additional methods. You can even call that str, using this sort of idiom:

class str(str):
    def spaminate(self):
        return "spam" * len(self)

s = str("hello")
print(s.spaminate())

But these are carefully restricted to the module in which you do them (and any module you import them into), and are restricted to clear uses of the name that you have overridden. String literals are never going to be affected.

5 Likes

If you want to add methods to built-in classes, then why not use dispatch (e.g., functools.singledispatch)?

1 Like

This was discussed at length on the python-ideas mailing list in the form of extension methods.

While I love this feature in Kotlin, I am not really convinced it would be a good fit for Python.

It may be helpful to read through the discussion there in case you want to address some of the concerns.

1 Like

thanks for replaying, but you are so aggressive
you can explain your opinion without saying No tanks lol.

i never tested that before, but i will give a try thanks for your attention :slight_smile:

1 Like

Hi Ahmed,

You can do the same thing as follows:

def do(self): 
    print('Class attribute "b" is', self.b)
A.do = do

But I think the nice thing about using decorators is that you can easily grep the patch location.

1 Like

While the implementation the OP uses monkeypatching, which is just the quick and dirty way to do this, I think the idea of traits is quite nice and it would be interesting addition to Python. Not having to subclass a class to add methods in a structured way seems very powerful to me, but I have not used much Rust.

@samuelcolvin since you’re an experienced Rust user, what is your opinion on this? Would traits add enough to Python to make the idea worth chasing?

I know python is dynamic and i can path any type of variable to any method however, it use type hints with different type
And also traits use to path different types to functions in static language but
It could give collection of classes a sheared property or method that could be use inside the function without fearing of getting an error because, every possible type can implement the same trait and so they all have the same method and it can work normally well with no errors and get the right output
We will never need to check type like is instance or what ever and make special case or implementation for every possible type of argument that path the function
I think it will give python more powerful way and much faster than waiting for checking the types or whatever

I have to back to class implementation and edit it to make this method work i think

I’m not sure what you mean by “back to class implementation and edit it to make this method work”.
It may be quick and dirty, but also an effective way to patch third-party packages until the bug is fixed or my desired feature is merged.

Having a way to add functions to a class may be an interesting way to use protocols. You could implement protocols for classes outside the file or module it is defined in :

from typing import Protocol
import inspect


class Edible(Protocol):
    def eat(self):
        ...


class Apple:
    pass


def impl(implemented, protocol=None):
    def decorator(implementor):
        if protocol is not None:
            # TODO: verify that types of protocol and implemented match
            pass
        for name, method in inspect.getmembers(implementor, inspect.isfunction):
            setattr(implemented, name, method)

    return decorator


@impl(Apple, Edible)
class EdibleApple:
    def eat(self):
        print("Eat the Apple")


if __name__ == "__main__":
    apple = Apple()
    apple.eat()

However, it could lead to strange situations if you implement the same protocol in multiple places (I think it is the reason rust forbids implementing traits outside the Trait mod or the Struct mod). It would also cause issue if you override functions with the same name.
And you would have to import the module where the @impl is defined to use those methods.

It is better to have the following syntax for this feature:

def A.do(self): 
    print('Class attribute "b" is', self.b)