[Python-ideas] Pseudo methods
Steven D'Aprano
steve at pearwood.info
Fri Aug 4 10:37:01 EDT 2017
On Fri, Aug 04, 2017 at 10:20:55AM -0300, Joao S. O. Bueno wrote:
> Had not this been discussed here earlier this year?
>
> (And despite there being perceived dangers to readability in the long term,
> was accepted?)
>
> Here it is on an archive:
> https://mail.python.org/pipermail/python-ideas/2017-February/044551.html
I don't read this as the same proposal. For starters, I don't believe
that it was intended to allow monkey-patching of builtins.
Another is that the syntax is much more explicit about where the method
is going:
def MyClass.method(self, arg):
...
is clearly a method of MyClass.
There was, if I recall, some open discussion of whether arbitrary
assignment targets should be allowed:
def module.func(x or None)[23 + n].attr.__type__.method(self, arg):
...
or if we should intentionally limit the allowed syntax, like we do for
decorators. My vote is for intentionally limiting it to a single dotted
name, like MyClass.method.
> And anyway - along that discussion, despite dislikng the general idea, I
> got convinced that
> creating an outside method that makes "super" or "__class__" work was
> rather complicated.
Complicated is an understatement. It's horrid :-)
Here's the problem: we can successfully inject methods into a class:
# -----%<-----
class Parent:
def spam(self):
return "spam"
class Child(Parent):
def food(self):
return 'yummy ' + self.spam()
c = Child()
c.food() # returns 'yummy spam' as expected
# inject a new method
def spam(self):
return 'spam spam spam'
Child.spam = spam
c.food() # returns 'yummy spam spam spam' as expected
# -----%<-----
But not if you use the zero-argument form of super():
# -----%<-----
del Child.spam # revert to original
def spam(self):
s = super().spam()
return ' '.join([s]*3)
Child.spam = spam
c.food()
# -----%<-----
This raises:
RuntimeError: super(): __class__ cell not found
This is the simplest thing I've found that will fix it:
# -----%<-----
del Child.spam # revert to original again
def outer():
__class__ = Child
def spam(self):
s = super().spam()
return ' '.join([s]*3)
return spam
Child.spam = outer()
c.food() # returns 'yummy spam spam spam' as expected
# -----%<-----
It's probably possibly to wrap this up in a decorator that takes Child
as argument, but I expect it will probably require messing about with
the undocumented FunctionType constructor to build up a new closure from
the bits and pieces scavenged from the decorated function.
> Maybe we could just have a decorator for that, that would properly create
> the __class__ cell?
I expect its possible. A challenge to somebody who wants to get their
hands dirty.
--
Steve
More information about the Python-ideas
mailing list