Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Python > Method default argument whose type is the class not yet defined

Reply
Thread Tools

Method default argument whose type is the class not yet defined

 
 
Jennie
Guest
Posts: n/a
 
      11-10-2012
What is the best solution to solve the following problem in Python 3.3?

import math
>>> class Point:

.... def __init__(self, x=0, y=0):
.... self.x = x
.... self.y = y
.... def __sub__(self, other):
.... return Point(self.x - other.x, self.y - other.y)
.... def distance(self, point=Point()):
.... """Return the distance from `point`."""
.... return math.sqrt((self - point).x ** 2 + (self - point).y ** 2)
....
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in Point
NameError: name 'Point' is not defined

I propose three solutions. The first one:

>>> class Point:

.... def __init__(self, x=0, y=0):
.... self.x = x
.... self.y = y
.... def __sub__(self, other):
.... return Point(self.x - other.x, self.y - other.y)
.... def distance(self, point=None):
.... p = point if point else Point()
.... return math.sqrt((self - p).x ** 2 + (self - p).y ** 2)
....
>>> p = Point()
>>> p.distance()

0.0
>>> p.distance(Point(3, 4))

5.0

The second one:

>>> class Point:

.... def __init__(self, x=0, y=0):
.... self.x = x
.... self.y = y
.... def __sub__(self, other):
.... return Point(self.x - other.x, self.y - other.y)
....
>>> def distance(self, point=Point()):

.... return math.sqrt((self - point).x ** 2 + (self - point).y ** 2)
....
>>> Point.distance = distance
>>> p = Point()
>>> p.distance(Point(3, 4))

5.0

The last one:

>>> class Point:

.... def __init__(self, x=0, y=0):
.... self.x = x
.... self.y = y
.... Point.distance = distance
.... def __sub__(self, other):
.... return Point(self.x - other.x, self.y - other.y)
....
>>> def distance(self, point=Point()):

.... return math.sqrt((self - point).x ** 2 + (self - point).y ** 2)
....
>>> p = Point()
>>> p.distance(Point(3, 4))

5.0

Is there a better solution?
--
Jennie
 
Reply With Quote
 
 
 
 
Chris Angelico
Guest
Posts: n/a
 
      11-10-2012
On Sun, Nov 11, 2012 at 6:33 AM, Jennie <(E-Mail Removed)> wrote:
> ... def distance(self, point=None):
> ... p = point if point else Point()


I'd go with this one. Definitely not the third one, which mutates the
class according to a current global every time a Point is instantiated
- could be *extremely* confusing if the name distance were ever
rebound. You could also fiddle with the default args:

>>> class Point:

def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def distance(self, point="Point()"):
return math.sqrt((self - p).x ** 2 + (self - p).y ** 2)

>>> Point.distance.__defaults__=Point(), # has to be a tuple


ChrisA
 
Reply With Quote
 
 
 
 
Terry Reedy
Guest
Posts: n/a
 
      11-10-2012
On 11/10/2012 2:33 PM, Jennie wrote:
> What is the best solution to solve the following problem in Python 3.3?
>
> import math
> >>> class Point:

> ... def __init__(self, x=0, y=0):
> ... self.x = x
> ... self.y = y
> ... def __sub__(self, other):
> ... return Point(self.x - other.x, self.y - other.y)
> ... def distance(self, point=Point()):
> ... """Return the distance from `point`."""
> ... return math.sqrt((self - point).x ** 2 + (self - point).y ** 2)
> ...
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 5, in Point
> NameError: name 'Point' is not defined
>
> I propose three solutions. The first one:
>
> >>> class Point:

> ... def __init__(self, x=0, y=0):
> ... self.x = x
> ... self.y = y
> ... def __sub__(self, other):
> ... return Point(self.x - other.x, self.y - other.y)
> ... def distance(self, point=None):
> ... p = point if point else Point()
> ... return math.sqrt((self - p).x ** 2 + (self - p).y ** 2)
> ...
> >>> p = Point()
> >>> p.distance()

> 0.0
> >>> p.distance(Point(3, 4))

> 5.0


What I do not like about this one is that it creates a new 0 point each
time one is needed. Two solutions:

change Point() to point0 in the distance function and create
point0 = Point()
after the class.

-or-
instead of p = line,
px,py = point.x, point.y if point else 0.0, 0.0


> The second one:
>
> >>> class Point:

> ... def __init__(self, x=0, y=0):
> ... self.x = x
> ... self.y = y
> ... def __sub__(self, other):
> ... return Point(self.x - other.x, self.y - other.y)
> ...
> >>> def distance(self, point=Point()):

> ... return math.sqrt((self - point).x ** 2 + (self - point).y ** 2)
> ...
> >>> Point.distance = distance
> >>> p = Point()
> >>> p.distance(Point(3, 4))

> 5.0


my first thought


> The last one:
>
> >>> class Point:

> ... def __init__(self, x=0, y=0):
> ... self.x = x
> ... self.y = y
> ... Point.distance = distance
> ... def __sub__(self, other):
> ... return Point(self.x - other.x, self.y - other.y)
> ...
> >>> def distance(self, point=Point()):

> ... return math.sqrt((self - point).x ** 2 + (self - point).y ** 2)
> ...
> >>> p = Point()
> >>> p.distance(Point(3, 4))

> 5.0
>
> Is there a better solution?



--
Terry Jan Reedy

 
Reply With Quote
 
Jennie
Guest
Posts: n/a
 
      11-10-2012
On 11/10/2012 09:29 PM, Terry Reedy wrote:

> On 11/10/2012 2:33 PM, Jennie wrote:
>>
>> I propose three solutions. The first one:
>>
>> >>> class Point:

>> ... def __init__(self, x=0, y=0):
>> ... self.x = x
>> ... self.y = y
>> ... def __sub__(self, other):
>> ... return Point(self.x - other.x, self.y - other.y)
>> ... def distance(self, point=None):
>> ... p = point if point else Point()
>> ... return math.sqrt((self - p).x ** 2 + (self - p).y ** 2)


> What I do not like about this one is that it creates a new 0 point each
> time one is needed. Two solutions:
>
> change Point() to point0 in the distance function and create
> point0 = Point()
> after the class.
>
> -or-
> instead of p = line,
> px,py = point.x, point.y if point else 0.0, 0.0


Thanks, I like the second one

--
Jennie
 
Reply With Quote
 
Jennie
Guest
Posts: n/a
 
      11-10-2012
On 11/10/2012 09:29 PM, Terry Reedy wrote:

> On 11/10/2012 2:33 PM, Jennie wrote:
>>
>> I propose three solutions. The first one:
>>
>> >>> class Point:

>> ... def __init__(self, x=0, y=0):
>> ... self.x = x
>> ... self.y = y
>> ... def __sub__(self, other):
>> ... return Point(self.x - other.x, self.y - other.y)
>> ... def distance(self, point=None):
>> ... p = point if point else Point()
>> ... return math.sqrt((self - p).x ** 2 + (self - p).y ** 2)


> What I do not like about this one is that it creates a new 0 point each
> time one is needed. Two solutions:
>
> change Point() to point0 in the distance function and create
> point0 = Point()
> after the class.
>
> -or-
> instead of p = line,
> px,py = point.x, point.y if point else 0.0, 0.0


Thanks, I like the second one

--
Jennie
 
Reply With Quote
 
Dave Angel
Guest
Posts: n/a
 
      11-10-2012
On 11/10/2012 03:51 PM, Jennie wrote:
> On 11/10/2012 09:29 PM, Terry Reedy wrote:
>
>> On 11/10/2012 2:33 PM, Jennie wrote:
>>>
>>> I propose three solutions. The first one:
>>>
>>> >>> class Point:
>>> ... def __init__(self, x=0, y=0):
>>> ... self.x = x
>>> ... self.y = y
>>> ... def __sub__(self, other):
>>> ... return Point(self.x - other.x, self.y - other.y)
>>> ... def distance(self, point=None):
>>> ... p = point if point else Point()
>>> ... return math.sqrt((self - p).x ** 2 + (self - p).y ** 2)

>
>> What I do not like about this one is that it creates a new 0 point each
>> time one is needed. Two solutions:
>>
>> change Point() to point0 in the distance function and create
>> point0 = Point()
>> after the class.
>>
>> -or-
>> instead of p = line,
>> px,py = point.x, point.y if point else 0.0, 0.0

>
> Thanks, I like the second one
>

I like the first, once you fix the minor inefficiency in it; add the
qualifier "is None"


.... def distance(self, point=None):
.... p = point if point is None else Point()
.... return math.sqrt((self - p).x ** 2 + (self - p).y ** 2)

The advantage it then has over the second one is that the whole class is
defined inside the class.

--

DaveA


 
Reply With Quote
 
Steven D'Aprano
Guest
Posts: n/a
 
      11-11-2012
On Sat, 10 Nov 2012 20:33:05 +0100, Jennie wrote:

[...]
> I propose three solutions. The first one:
>
> >>> class Point:

> ... def __init__(self, x=0, y=0):
> ... self.x = x
> ... self.y = y
> ... def __sub__(self, other):
> ... return Point(self.x - other.x, self.y - other.y)


Don't do this, because it breaks subclassing. Your instance should
dynamically get it's own class, not hard-code the name of Point.

return self.__class__(self.x - other.x, self.y - other.y)

That way, when you subclass Point, you can do arithmetic on the subclass
instances and they will do the Right Thing.

Note: Python's builtin numeric types don't do this, and it is a damned
nuisance:

py> class MyInt(int):
.... pass
....
py> a, b = MyInt(23), MyInt(42)
py> assert type(a) is MyInt and type(b) is MyInt
py> type(a + b)
<type 'int'>


Back to your class:

> ... def distance(self, point=None):
> ... p = point if point else Point()
> ... return math.sqrt((self - p).x ** 2 + (self - p).y ** 2)


Almost but not quite. I assume that, in a full Point class, you would
want Point(0, 0) to count as false in a boolean context. (A "falsey"
value, like None, [], 0.0, etc.) So change the test to an explicit test
for None, not just any falsey value:

if point is None:
point = self.__class__() # Allow subclassing to work.


> The second one:
>
> >>> class Point:

> ... def __init__(self, x=0, y=0):
> ... self.x = x
> ... self.y = y
> ... def __sub__(self, other):
> ... return Point(self.x - other.x, self.y - other.y)
> ...
> >>> def distance(self, point=Point()):

> ... return math.sqrt((self - point).x ** 2 + (self - point).y ** 2)
> ...
> >>> Point.distance = distance


Cute, but ugly and messy. You can inject methods into a class, of course,
but that's an awfully big hammer to crack this tiny little nut. Your
first solution is better.

Here is a variation which, according to your tastes, may count as more or
less ugly: inject the default value into the method:

class Point:
def distance(self, other=None): # None is a placeholder
delta = self - other
return math.sqrt(delta.x ** 2 + delta.y ** 2)

Point.distance.__defaults__ = (Point(),)
# In Python 2, use:
# Point.distance.__func__.func_defaults = (Point(),)



--
Steven
 
Reply With Quote
 
Oscar Benjamin
Guest
Posts: n/a
 
      11-11-2012
On 10 November 2012 19:33, Jennie <(E-Mail Removed)> wrote:
> What is the best solution to solve the following problem in Python 3.3?
>
> import math
>>>> class Point:

> ... def __init__(self, x=0, y=0):
> ... self.x = x
> ... self.y = y
> ... def __sub__(self, other):
> ... return Point(self.x - other.x, self.y - other.y)
> ... def distance(self, point=Point()):
> ... """Return the distance from `point`."""
> ... return math.sqrt((self - point).x ** 2 + (self - point).y ** 2)
> ...
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "<stdin>", line 5, in Point
> NameError: name 'Point' is not defined


I would use namedtuple and make it so that an ordinary tuple could be
used as in place of a Point instance:

>>> import math
>>> from collections import namedtuple
>>> class Point(namedtuple('Point', ['x', 'y'])):

.... def distance(self, other=(0, 0)):
.... (myx, myy), (theirx, theiry) = self, other
.... return math.sqrt((myx - theirx) ** 2 + (myy - theiry) ** 2)
....
>>> p = Point(3, 4)
>>> p.distance()

5.0
>>> p2 = Point(4, 5)
>>> p.distance(p2)

1.4142135623730951


Oscar
 
Reply With Quote
 
Chris Angelico
Guest
Posts: n/a
 
      11-11-2012
On Sun, Nov 11, 2012 at 12:13 PM, Steven D'Aprano
<(E-Mail Removed)> wrote:
> Almost but not quite. I assume that, in a full Point class, you would
> want Point(0, 0) to count as false in a boolean context. (A "falsey"
> value, like None, [], 0.0, etc.)


I would not assume that. The origin is a point, just like any other.
With a Line class, you could deem a zero-length line to be like a
zero-element list, but Point(0,0) is more like the tuple (0,0) which
is definitely True. In any case, this would not even matter, beyond
unnecessary work; the bug would occur only if you seek the distance to
Point(0,0), at which point[1] the code would throw out the incoming
Point and go with the default of 0,0. So it'd result in the same
distance.

ChrisA

[1] Sorry, couldn't resist
 
Reply With Quote
 
Ian Kelly
Guest
Posts: n/a
 
      11-11-2012
On Sat, Nov 10, 2012 at 7:13 PM, Chris Angelico <(E-Mail Removed)> wrote:
> I would not assume that. The origin is a point, just like any other.
> With a Line class, you could deem a zero-length line to be like a
> zero-element list, but Point(0,0) is more like the tuple (0,0) which
> is definitely True.


It's more like the number 0 than the tuple (0,0).

0 is the origin on a 1-dimensional number line.
(0,0) is the origin on a 2-dimensional number plane.

In fact, it might be pointed out that Point(0, 0) is a generalization
of 0+0j, which is equal to 0.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
VHDL Type Mismatch error indexed name returns a value whose type does not match programmingzeal VHDL 0 05-06-2012 06:38 AM
declaring a friend template function whose arguments are of type = private member type Hicham Mouline C++ 1 03-27-2009 03:58 PM
object as a argument of the method in which the method is defined ? mike C++ 5 02-22-2006 01:24 PM
#if (defined(__STDC__) && !defined(NO_PROTOTYPE)) || defined(__cplusplus) Oodini C Programming 1 09-27-2005 07:58 PM
Error *cannot return a value from method whose result type is void* Charles Java 6 07-28-2004 12:13 PM



Advertisments