[Tutor] Why is the name "self" optional instead of mandatory?
Steven D'Aprano
steve at pearwood.info
Sun Jan 24 20:40:49 EST 2016
On Fri, Jan 22, 2016 at 11:10:39PM -0600, boB Stepp wrote:
> On Thu, Jan 21, 2016 at 5:49 AM, Steven D'Aprano <steve at pearwood.info> wrote:
> > class X:
> > pass
> >
> > def func(this):
> > print("instance %r called method" % this)
> >
> > X.method = func
>
> Am I understanding this correctly? It appears to me that you have
> successfully added a method to class X from outside of class X! If
> this is the case, then this clarifies some things I was wondering
> about in my other thread.
Yes, you are reading it correctly.
Of course, in practice you wouldn't do that as shown. Why write the
method outside the class when you could write it inside the class? There
are some good reasons for doing something similar though:
(1) Perhaps you have a whole family of classes that share the same
method. Three traditional solutions to this are to use inheritence, a
mixin class, or traits. But a fourth is to create your classes without
the method, then dynamically add the extra, shared, method into each one
afterwards.
Possibly by using a simple decorator:
def method(self, arg): ...
def decorate(cls):
cls.method = method
return cls
@decorate
class Spam: ...
This technique is even more powerful when the method you are injecting
is *slightly different* each time. For that, you can use a closure,
created *inside* the decorator function. Play around with this example
and see if you can understand what is going on:
# Warning: this may expand your mind.
def decorate(number):
# Create a closure over the number.
def method(self, x):
"""Return x + %d."""
return x + number
method.__doc__ %= number
# Create a decorator.
def decorator(cls):
# Inject the method into the class.
cls.method = method
return cls
# Return the decorator so it can be used.
return decorator
@decorate(5)
class AddFive:
pass
@decorate(9)
class AddNine:
pass
(2) Another reason for doing this may be that you have an existing class
from some library that you have to use. You can't subclass it, because
too much of your code already depends on using that specific class. In
some languages, like Java, perhaps the library authors marked the class
as unsubclassable. But you want to add a new method for your own use.
Here's an example of this:
http://stackoverflow.com/questions/13730924/java-adding-fields-and-methods-to-existing-class
You'll notice that the answers given don't really solve the problem,
apart from a vague and scary-sounding suggestion to use "byte-code
injection". A number of people suggest subclassing, but a comment
from another person says the the question also applies to him, but in
his case subclassing isn't practical.
In Python, we have two solutions:
Write a function, and use that. Instead of calling obj.new_method(),
we simply have new_function(obj). This option is available to Java as
well, but hardly anyone ever thinks of it because Java is the Kingdom of
Nouns:
http://steve-yegge.blogspot.com.au/2006/03/execution-in-kingdom-of-nouns.html
Or *monkey-patch* the class using a new method we create on the outside.
https://en.wikipedia.org/wiki/Monkey_patch
Monkey-patching is a powerful technique, but should be used with care.
Overuse is considered harmful:
http://devblog.avdi.org/2008/02/23/why-monkeypatching-is-destroying-ruby/
[...]
> I guess I am trying to wrap my mind around this incredible power and
> flexibility that Python provides. I thought I had asked a relatively
> harmless question. But it generated some strong responses! It seemed
> like "self" had a similar utility of use as "print" to me. After all,
> we can't redefine "print", can we? But now I realize that I can do
> exactly that if I so choose. That is both fascinating and scary!
Indeed. And in Python 3, print is a regular function, which means it
*can* be redefined by shadowing, or even by monkey-patching the
built-in module.
--
Steve
More information about the Tutor
mailing list