Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return self in python [closed]

I have a class that represents object. And I have a bunch of methods which modify this object state with no obvious return or obviously without any return. In C# I would declare all these methods as void and see no alternatives. But in Python I am about to make all the methods return self to give myself ability to write awesome one-liners like this:

classname().method1().method2().method3()

Is this Pythonic, or otherwise acceptable in Python?

like image 971
Philip B Avatar asked Mar 27 '16 18:03

Philip B


People also ask

What happens when you return self in Python?

return self would return the object that the method was called from.

Can you return self in Python?

If a method can fail, it should return success or failure or raise an exception to be handled. One caveat is that if you use the return self idiom, Python will allow you to assign all your methods to variables and you might think you are getting a data result or a list when you are actually getting the object.

Why is self not working in Python?

Python Class self ConstructorIf we have a variable inside a method, in this case, self will not work. That variable exists only while that method is executing and is thus unique to that method. To create global fields or variables for the whole class, we must declare them outside of the class methods.

What does * self mean in Python?

self represents the instance of the class. By using the “self” we can access the attributes and methods of the class in python. It binds the attributes with the given arguments. The reason you need to use self. is because Python does not use the @ syntax to refer to instance attributes.


2 Answers

Here is a mail from Guido van Rossum (the author of the Python programming language) about this topic: https://mail.python.org/pipermail/python-dev/2003-October/038855.html

I'd like to explain once more why I'm so adamant that sort() shouldn't return 'self'.

This comes from a coding style (popular in various other languages, I believe especially Lisp revels in it) where a series of side effects on a single object can be chained like this:

x.compress().chop(y).sort(z)

which would be the same as

x.compress() x.chop(y) x.sort(z)

I find the chaining form a threat to readability; it requires that the reader must be intimately familiar with each of the methods. The second form makes it clear that each of these calls acts on the same object, and so even if you don't know the class and its methods very well, you can understand that the second and third call are applied to x (and that all calls are made for their side-effects), and not to something else.

I'd like to reserve chaining for operations that return new values, like string processing operations:

y = x.rstrip("\n").split(":").lower()

There are a few standard library modules that encourage chaining of side-effect calls (pstat comes to mind). There shouldn't be any new ones; pstat slipped through my filter when it was weak.

like image 133
Querenker Avatar answered Sep 30 '22 19:09

Querenker


It is an excellent idea for APIs where you are building state through methods. SQLAlchemy uses this to great effect for example:

>>> from sqlalchemy.orm import aliased
>>> adalias1 = aliased(Address)
>>> adalias2 = aliased(Address)
>>> for username, email1, email2 in \
...     session.query(User.name, adalias1.email_address, adalias2.email_address).\
...     join(adalias1, User.addresses).\
...     join(adalias2, User.addresses).\
...     filter(adalias1.email_address=='[email protected]').\
...     filter(adalias2.email_address=='[email protected]'):
...     print(username, email1, email2)

Note that it doesn't return self in many cases; it will return a clone of the current object with a certain aspect altered. This way you can create divergent chains based of a shared base; base = instance.method1().method2(), then foo = base.method3() and bar = base.method4().

In the above example, the Query object returned by a Query.join() or Query.filter() call is not the same instance, but a new instance with the filter or join applied to it.

It uses a Generative base class to build upon; so rather than return self, the pattern used is:

def method(self):
    clone = self._generate()
    clone.foo = 'bar'
    return clone

which SQLAlchemy further simplified by using a decorator:

def _generative(func):
    @wraps(func)
    def decorator(self, *args, **kw):
        new_self = self._generate()
        func(new_self, *args, **kw)
        return new_self
    return decorator

class FooBar(GenerativeBase):
    @_generative
    def method(self):
        self.foo = 'bar'

All the @_generative-decorated method has to do is make the alterations on the copy, the decorator takes care of producing the copy, binding the method to the copy rather than the original, and returning it to the caller for you.

like image 26
Martijn Pieters Avatar answered Sep 30 '22 19:09

Martijn Pieters