Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #84510 > unrolled thread
| Started by | Brian Gladman <noone@nowhere.net> |
|---|---|
| First post | 2015-01-24 22:57 +0000 |
| Last post | 2015-01-25 07:43 +0000 |
| Articles | 16 — 5 participants |
Back to article view | Back to comp.lang.python
Delegation in Python Brian Gladman <noone@nowhere.net> - 2015-01-24 22:57 +0000
Re: Delegation in Python Chris Angelico <rosuav@gmail.com> - 2015-01-25 10:22 +1100
Re: Delegation in Python Brian Gladman <noone@nowhere.net> - 2015-01-24 23:38 +0000
Re: Delegation in Python Chris Angelico <rosuav@gmail.com> - 2015-01-25 10:43 +1100
Re: Delegation in Python Brian Gladman <noone@nowhere.net> - 2015-01-25 00:18 +0000
Re: Delegation in Python Chris Angelico <rosuav@gmail.com> - 2015-01-25 11:28 +1100
Re: Delegation in Python Brian Gladman <noone@nowhere.net> - 2015-01-25 07:49 +0000
Re: Delegation in Python Chris Angelico <rosuav@gmail.com> - 2015-01-25 19:07 +1100
Re: Delegation in Python Gary Herron <gherron@digipen.edu> - 2015-01-24 15:47 -0800
Re: Delegation in Python Brian Gladman <noone@nowhere.net> - 2015-01-24 23:58 +0000
Re: Delegation in Python Gary Herron <gherron@digipen.edu> - 2015-01-24 15:41 -0800
Re: Delegation in Python Brian Gladman <noone@nowhere.net> - 2015-01-24 23:52 +0000
Re: Delegation in Python Mark Lawrence <breamoreboy@yahoo.co.uk> - 2015-01-24 23:59 +0000
Re: Delegation in Python Chris Angelico <rosuav@gmail.com> - 2015-01-25 11:07 +1100
Re: Delegation in Python Terry Reedy <tjreedy@udel.edu> - 2015-01-24 20:31 -0500
Re: Delegation in Python Brian Gladman <noone@nowhere.net> - 2015-01-25 07:43 +0000
| From | Brian Gladman <noone@nowhere.net> |
|---|---|
| Date | 2015-01-24 22:57 +0000 |
| Subject | Delegation in Python |
| Message-ID | <FeCdnXdsTpunvlnJnZ2dnUVZ8judnZ2d@brightview.co.uk> |
I would appreciate advice on how to set up delgation in Python.
I am continuously implementing a function to test whether a Python
Fraction is an integer so I wanted to define a new class, based on
Fraction, that includes this new method.
But I am not clear on how to delegate from my new class to the existing
Fraction class. This is what I have:
--------------------------
class RF(Fraction):
def __new__(self, x, y):
super().__new__(self, x, y)
def is_integer(self):
return self.numerator % self.denominator == 0
def __getattr__(self, attr):
return getattr(self, attr)
--------------------------
which doesn't work.
Any advice on how to do this would be much appreciated.
Brian
[toc] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-25 10:22 +1100 |
| Message-ID | <mailman.18106.1422141738.18130.python-list@python.org> |
| In reply to | #84510 |
On Sun, Jan 25, 2015 at 9:57 AM, Brian Gladman <noone@nowhere.net> wrote:
> But I am not clear on how to delegate from my new class to the existing
> Fraction class. This is what I have:
>
> --------------------------
> class RF(Fraction):
>
> def __new__(self, x, y):
> super().__new__(self, x, y)
>
> def is_integer(self):
> return self.numerator % self.denominator == 0
>
> def __getattr__(self, attr):
> return getattr(self, attr)
If you just drop everything but your new method, it should work just fine.
class RF(Fraction):
def is_integer(self):
return self.numerator % self.denominator == 0
However, this doesn't ensure that operations on RFs will return more
RFs - they'll often return Fractions instead. There's no easy fix for
that, sorry.
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Brian Gladman <noone@nowhere.net> |
|---|---|
| Date | 2015-01-24 23:38 +0000 |
| Message-ID | <5ZKdnd6rbdZ3sVnJnZ2dnUVZ8mCdnZ2d@brightview.co.uk> |
| In reply to | #84514 |
On 24/01/2015 23:22, Chris Angelico wrote: > class RF(Fraction): > def is_integer(self): > return self.numerator % self.denominator == 0 Thanks for your help on this. I must admit that nowhere in a lot of searching did I find that delegation is achieved by doing nothing! Brian
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-25 10:43 +1100 |
| Message-ID | <mailman.18109.1422143020.18130.python-list@python.org> |
| In reply to | #84516 |
On Sun, Jan 25, 2015 at 10:38 AM, Brian Gladman <noone@nowhere.net> wrote: > On 24/01/2015 23:22, Chris Angelico wrote: >> class RF(Fraction): >> def is_integer(self): >> return self.numerator % self.denominator == 0 > > Thanks for your help on this. I must admit that nowhere in a lot of > searching did I find that delegation is achieved by doing nothing! Hehe :) If you want a technical look at it, what you have is the fundamental nature of subclassing: you derive from another class, and your class is identical to that with certain exceptions (in your case, an additional method). Python's way of handling that is called the Method Resolution Order or MRO, and you can find info on it on the web, eg: http://python-history.blogspot.com/2010/06/method-resolution-order.html Enjoy! ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Brian Gladman <noone@nowhere.net> |
|---|---|
| Date | 2015-01-25 00:18 +0000 |
| Message-ID | <K8KdncuoKPL1q1nJnZ2dnUVZ8h2dnZ2d@brightview.co.uk> |
| In reply to | #84519 |
On 24/01/2015 23:43, Chris Angelico wrote: > On Sun, Jan 25, 2015 at 10:38 AM, Brian Gladman <noone@nowhere.net> wrote: >> On 24/01/2015 23:22, Chris Angelico wrote: >>> class RF(Fraction): >>> def is_integer(self): >>> return self.numerator % self.denominator == 0 >> >> Thanks for your help on this. I must admit that nowhere in a lot of >> searching did I find that delegation is achieved by doing nothing! > > Hehe :) If you want a technical look at it, what you have is the > fundamental nature of subclassing: you derive from another class, and > your class is identical to that with certain exceptions (in your case, > an additional method). Python's way of handling that is called the > Method Resolution Order or MRO, and you can find info on it on the > web, eg: > > http://python-history.blogspot.com/2010/06/method-resolution-order.html Thanks for the further background. Is there a way of doing delegation rather than sub-classing? That is, can I create a class (say RF) that passes some of its methods to Fraction for implementation but always returns an RF? Brian
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-25 11:28 +1100 |
| Message-ID | <mailman.18116.1422145722.18130.python-list@python.org> |
| In reply to | #84530 |
On Sun, Jan 25, 2015 at 11:18 AM, Brian Gladman <noone@nowhere.net> wrote:
> Is there a way of doing delegation rather than sub-classing?
>
> That is, can I create a class (say RF) that passes some of its methods
> to Fraction for implementation but always returns an RF?
Hmm. The key here is that you want more than just delegation; you want
to transform every return value. That's not going to be easy. It would
be easiest and cleanest to skip the whole thing, and have a separate
function for what you're doing here - not a method, a stand-alone
function.
def is_integer(fr):
return fr.numerator % fr.denominator == 0
Next best would be the monkey-patching option. Delegation with
transformation is a lot of effort for what you want.
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Brian Gladman <noone@nowhere.net> |
|---|---|
| Date | 2015-01-25 07:49 +0000 |
| Message-ID | <KYednbizIP5rAlnJnZ2dnUVZ7sidnZ2d@brightview.co.uk> |
| In reply to | #84531 |
On 25/01/2015 00:28, Chris Angelico wrote: > On Sun, Jan 25, 2015 at 11:18 AM, Brian Gladman <noone@nowhere.net> wrote: >> Is there a way of doing delegation rather than sub-classing? >> >> That is, can I create a class (say RF) that passes some of its methods >> to Fraction for implementation but always returns an RF? > > Hmm. The key here is that you want more than just delegation; you want > to transform every return value. That's not going to be easy. It would > be easiest and cleanest to skip the whole thing, and have a separate > function for what you're doing here - not a method, a stand-alone > function. > > def is_integer(fr): > return fr.numerator % fr.denominator == 0 > > Next best would be the monkey-patching option. Delegation with > transformation is a lot of effort for what you want. Thanks, a part of this was a wish to understand how to map what I can do in other languages into Python. I felt that it might just be possible in Python to avoid having to wrap all the methods of the base class in the derived class. But it seems that __getattr__ etc are not quite as magic as I hoped. Thanks again for your help. Brian
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-25 19:07 +1100 |
| Message-ID | <mailman.18130.1422173260.18130.python-list@python.org> |
| In reply to | #84559 |
On Sun, Jan 25, 2015 at 6:49 PM, Brian Gladman <noone@nowhere.net> wrote:
> Thanks, a part of this was a wish to understand how to map what I can do
> in other languages into Python. I felt that it might just be possible
> in Python to avoid having to wrap all the methods of the base class in
> the derived class. But it seems that __getattr__ etc are not quite as
> magic as I hoped.
They do exactly what they're documented to, nothing more and nothing
less :) It's certainly possible to use them to hook into missing
attributes, for instance:
>>> class RF:
def __init__(self, *args, **kw):
self._frac = Fraction(*args, **kw)
def __getattr__(self, attr):
return getattr(self._frac, attr)
def __repr__(self):
return "RF(%d, %d)" % (self._frac.numerator, self._frac.denominator)
def is_integer(self):
return self._frac.denominator==1
>>> RF(1,4).numerator
1
But it doesn't work for everything:
>>> RF(1,4)*2
Traceback (most recent call last):
File "<pyshell#37>", line 1, in <module>
RF(1,4)*2
TypeError: unsupported operand type(s) for *: 'RF' and 'int'
The only solution would be to go through every operation that you care
about, and manually hook them. Something like this:
def __mul__(self, other):
result = self._frac * other
if isinstance(result, Fraction):
return RF(result.numerator, result.denominator)
return result
>>> RF(1,4)*2
RF(1, 2)
And do that for every other operation, method, etc. Tedious, but can
be effective if you need it.
ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Gary Herron <gherron@digipen.edu> |
|---|---|
| Date | 2015-01-24 15:47 -0800 |
| Message-ID | <mailman.18111.1422143281.18130.python-list@python.org> |
| In reply to | #84516 |
On 01/24/2015 03:38 PM, Brian Gladman wrote: > On 24/01/2015 23:22, Chris Angelico wrote: >> class RF(Fraction): >> def is_integer(self): >> return self.numerator % self.denominator == 0 > Thanks for your help on this. I must admit that nowhere in a lot of > searching did I find that delegation is achieved by doing nothing! > > Brian That's *not* "doing nothing". And it's not even really "delegation". It's just sub-classing Fraction to add one new method and inherit all other methods. Gary Herron -- Dr. Gary Herron Department of Computer Science DigiPen Institute of Technology (425) 895-4418
[toc] | [prev] | [next] | [standalone]
| From | Brian Gladman <noone@nowhere.net> |
|---|---|
| Date | 2015-01-24 23:58 +0000 |
| Message-ID | <2didnRFXOrIwrFnJnZ2dnUVZ8iednZ2d@brightview.co.uk> |
| In reply to | #84521 |
On 24/01/2015 23:47, Gary Herron wrote: > On 01/24/2015 03:38 PM, Brian Gladman wrote: >> On 24/01/2015 23:22, Chris Angelico wrote: >>> class RF(Fraction): >>> def is_integer(self): >>> return self.numerator % self.denominator == 0 >> Thanks for your help on this. I must admit that nowhere in a lot of >> searching did I find that delegation is achieved by doing nothing! >> >> Brian > > That's *not* "doing nothing". And it's not even really "delegation". > It's just sub-classing Fraction to add one new method and inherit all > other methods. I realised that - I was being whimsical. I really did want true delgation so that I get an RF returned even when I delegate to Fractions. Brian
[toc] | [prev] | [next] | [standalone]
| From | Gary Herron <gherron@digipen.edu> |
|---|---|
| Date | 2015-01-24 15:41 -0800 |
| Message-ID | <mailman.18108.1422142883.18130.python-list@python.org> |
| In reply to | #84510 |
On 01/24/2015 03:22 PM, Chris Angelico wrote: > On Sun, Jan 25, 2015 at 9:57 AM, Brian Gladman <noone@nowhere.net> wrote: >> But I am not clear on how to delegate from my new class to the existing >> Fraction class. This is what I have: >> >> -------------------------- >> class RF(Fraction): >> >> def __new__(self, x, y): >> super().__new__(self, x, y) >> >> def is_integer(self): >> return self.numerator % self.denominator == 0 >> >> def __getattr__(self, attr): >> return getattr(self, attr) > If you just drop everything but your new method, it should work just fine. > > class RF(Fraction): > def is_integer(self): > return self.numerator % self.denominator == 0 > > However, this doesn't ensure that operations on RFs will return more > RFs - they'll often return Fractions instead. There's no easy fix for > that, sorry. > > ChrisA You can always "monkey-path" the Fraction class on the fly to add a new method to it. I think most would consider this a bad idea, but it does work. Try this: >>> from fractions import Fraction >>> def is_integer(self): ... return self.numerator % self.denominator == 0 ... >>> Fraction.is_integer = is_integer # Monkey-patch Fraction >>> >>> Fraction(1,2).is_integer() False >>> Fraction(2,1).is_integer() True Gary Herron -- Dr. Gary Herron Department of Computer Science DigiPen Institute of Technology (425) 895-4418
[toc] | [prev] | [next] | [standalone]
| From | Brian Gladman <noone@nowhere.net> |
|---|---|
| Date | 2015-01-24 23:52 +0000 |
| Message-ID | <UridnbK49sm5rVnJnZ2dnUVZ7radnZ2d@brightview.co.uk> |
| In reply to | #84518 |
On 24/01/2015 23:41, Gary Herron wrote: [snip]> > You can always "monkey-path" the Fraction class on the fly to add a new > method to it. I think most would consider this a bad idea, but it does > work. > Try this: > >>>> from fractions import Fraction >>>> def is_integer(self): > ... return self.numerator % self.denominator == 0 > ... >>>> Fraction.is_integer = is_integer # Monkey-patch Fraction >>>> >>>> Fraction(1,2).is_integer() > False >>>> Fraction(2,1).is_integer() > True Thanks Gary. As Chris says, the method he suggests is problematic since anything delegated returns a Fraction, not an RF. Patching Fraction looks nicer to use even if it is frowned on. Brian
[toc] | [prev] | [next] | [standalone]
| From | Mark Lawrence <breamoreboy@yahoo.co.uk> |
|---|---|
| Date | 2015-01-24 23:59 +0000 |
| Message-ID | <mailman.18113.1422144008.18130.python-list@python.org> |
| In reply to | #84510 |
On 24/01/2015 23:41, Gary Herron wrote: > On 01/24/2015 03:22 PM, Chris Angelico wrote: >> On Sun, Jan 25, 2015 at 9:57 AM, Brian Gladman <noone@nowhere.net> wrote: >>> But I am not clear on how to delegate from my new class to the existing >>> Fraction class. This is what I have: >>> >>> -------------------------- >>> class RF(Fraction): >>> >>> def __new__(self, x, y): >>> super().__new__(self, x, y) >>> >>> def is_integer(self): >>> return self.numerator % self.denominator == 0 >>> >>> def __getattr__(self, attr): >>> return getattr(self, attr) >> If you just drop everything but your new method, it should work just >> fine. >> >> class RF(Fraction): >> def is_integer(self): >> return self.numerator % self.denominator == 0 >> >> However, this doesn't ensure that operations on RFs will return more >> RFs - they'll often return Fractions instead. There's no easy fix for >> that, sorry. >> >> ChrisA > > You can always "monkey-path" the Fraction class on the fly to add a new > method to it. I think most would consider this a bad idea, but it does > work. > Try this: > > >>> from fractions import Fraction > >>> def is_integer(self): > ... return self.numerator % self.denominator == 0 > ... > >>> Fraction.is_integer = is_integer # Monkey-patch Fraction > >>> > >>> Fraction(1,2).is_integer() > False > >>> Fraction(2,1).is_integer() > True > > > Gary Herron > As regards this being a bad idea I'd suggest the latest score is Practicality 1 Purity 0 :) -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence
[toc] | [prev] | [next] | [standalone]
| From | Chris Angelico <rosuav@gmail.com> |
|---|---|
| Date | 2015-01-25 11:07 +1100 |
| Message-ID | <mailman.18115.1422144482.18130.python-list@python.org> |
| In reply to | #84510 |
On Sun, Jan 25, 2015 at 10:59 AM, Mark Lawrence <breamoreboy@yahoo.co.uk> wrote: >> You can always "monkey-path" the Fraction class on the fly to add a new >> method to it. I think most would consider this a bad idea, but it does >> work. > > As regards this being a bad idea I'd suggest the latest score is > Practicality 1 Purity 0 :) Indeed. But there's a huge difference between simple and complex projects. I had a bit of a nightmare trying to figure out what was going on with a project's logging... it looked like the Python logging module, but there was another argument being processed, courtesy of some monkey-patching. Try to keep these changes to small projects, where it's easier to keep everything in your head; or for a temporary bit of debugging, where you just want to do this temporarily and then undo it again when you find the bug. Otherwise, it gets confusing for the subsequent reader. ChrisA
[toc] | [prev] | [next] | [standalone]
| From | Terry Reedy <tjreedy@udel.edu> |
|---|---|
| Date | 2015-01-24 20:31 -0500 |
| Message-ID | <mailman.18120.1422149503.18130.python-list@python.org> |
| In reply to | #84510 |
On 1/24/2015 5:57 PM, Brian Gladman wrote: > I would appreciate advice on how to set up delgation in Python. > > I am continuously implementing a function to test whether a Python > Fraction is an integer Since Fractions are reduced to lowest terms, >>> from fractions import Fraction as F >>> F(4, 2) Fraction(2, 1) >>> F(12, 3) Fraction(4, 1) the test is that the denominator is 1 >>> F(12,3).denominator == 1 True >>> F(12,5).denominator == 1 False it may not be worth the bother to define a function, def fint(F): return F.denominator == 1 >so I wanted to define a new class, based on > Fraction, that includes this new method. but if you do, there is little need to make it a method. You are not overriding an existing method or adding a new special method. The math module has float and int functions that have *not* been turned into methods. Ditto for cmath. I would just leave the function a function. Python is not Java. -- Terry Jan Reedy
[toc] | [prev] | [next] | [standalone]
| From | Brian Gladman <noone@nowhere.net> |
|---|---|
| Date | 2015-01-25 07:43 +0000 |
| Message-ID | <_YmdnTx6rZsjA1nJnZ2dnUVZ8hOdnZ2d@brightview.co.uk> |
| In reply to | #84537 |
On 25/01/2015 01:31, Terry Reedy wrote: > On 1/24/2015 5:57 PM, Brian Gladman wrote: >> I would appreciate advice on how to set up delgation in Python. >> >> I am continuously implementing a function to test whether a Python >> Fraction is an integer > > Since Fractions are reduced to lowest terms, >>>> from fractions import Fraction as F >>>> F(4, 2) > Fraction(2, 1) >>>> F(12, 3) > Fraction(4, 1) > > the test is that the denominator is 1 Thanks Terry, I should have realised this but didn't. >>>> F(12,3).denominator == 1 > True >>>> F(12,5).denominator == 1 > False > > it may not be worth the bother to define a function, > > def fint(F): return F.denominator == 1 > >> so I wanted to define a new class, based on >> Fraction, that includes this new method. > > but if you do, there is little need to make it a method. You are not > overriding an existing method or adding a new special method. The math > module has float and int functions that have *not* been turned into > methods. Ditto for cmath. I would just leave the function a function. Yes, it seems so. Brian
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web