Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #18226 > unrolled thread

mutually exclusive arguments to a constructor

Started byAdam Funk <a24061@ducksburg.com>
First post2011-12-30 20:40 +0000
Last post2011-12-31 20:59 +0000
Articles 13 — 8 participants

Back to article view | Back to comp.lang.python


Contents

  mutually exclusive arguments to a constructor Adam Funk <a24061@ducksburg.com> - 2011-12-30 20:40 +0000
    Re: mutually exclusive arguments to a constructor "Günther Dietrich" <gd.usenet@spamfence.net> - 2011-12-30 22:00 +0100
      Re: mutually exclusive arguments to a constructor Adam Funk <a24061@ducksburg.com> - 2011-12-31 20:55 +0000
    Re: mutually exclusive arguments to a constructor Mel Wilson <mwilson@the-wire.com> - 2011-12-30 16:08 -0500
    Re: mutually exclusive arguments to a constructor Arnaud Delobelle <arnodel@gmail.com> - 2011-12-30 21:14 +0000
    Re: mutually exclusive arguments to a constructor Jason Friedman <jason@powerpull.net> - 2011-12-30 21:18 +0000
      Re: mutually exclusive arguments to a constructor Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-12-30 22:21 +0000
    Re: mutually exclusive arguments to a constructor Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-12-30 22:13 +0000
    Re: mutually exclusive arguments to a constructor Roy Smith <roy@panix.com> - 2011-12-30 18:24 -0500
      Re: mutually exclusive arguments to a constructor Chris Angelico <rosuav@gmail.com> - 2011-12-31 10:36 +1100
        Re: mutually exclusive arguments to a constructor Roy Smith <roy@panix.com> - 2011-12-30 18:39 -0500
          Re: mutually exclusive arguments to a constructor Chris Angelico <rosuav@gmail.com> - 2011-12-31 10:47 +1100
      Re: mutually exclusive arguments to a constructor Adam Funk <a24061@ducksburg.com> - 2011-12-31 20:59 +0000

#18226 — mutually exclusive arguments to a constructor

FromAdam Funk <a24061@ducksburg.com>
Date2011-12-30 20:40 +0000
Subjectmutually exclusive arguments to a constructor
Message-ID<g6k1t8xg0a.ln2@news.ducksburg.com>
(Warning: this question obviously reflects the fact that I am more
accustomed to using Java than Python.)

Suppose I'm creating a class that represents a bearing or azimuth,
created either from a string of traditional bearing notation
("N24d30mE") or from a number indicating the angle in degrees as
usually measured in trigonometry (65.5, measured counter-clockwise
from the x-axis).  The class will have methods to return the same
bearing in various formats.

In Java, I would write two constructors, one taking a single String
argument and one taking a single Double argument.  But in Python, a
class can have only one __init__ method, although it can have a lot of
optional arguments with default values.  What's the correct way to
deal with a situation like the one I've outlined above?


-- 
Unix is a user-friendly operating system. It's just very choosy about
its friends.

[toc] | [next] | [standalone]


#18228

From"Günther Dietrich" <gd.usenet@spamfence.net>
Date2011-12-30 22:00 +0100
Message-ID<gd.usenet-3A3312.22005830122011@dwarf.main.lan>
In reply to#18226
Adam Funk <a24061@ducksburg.com> wrote:

>Suppose I'm creating a class that represents a bearing or azimuth,
>created either from a string of traditional bearing notation
>("N24d30mE") or from a number indicating the angle in degrees as
>usually measured in trigonometry (65.5, measured counter-clockwise
>from the x-axis).  The class will have methods to return the same
>bearing in various formats.
>
>In Java, I would write two constructors, one taking a single String
>argument and one taking a single Double argument.  But in Python, a
>class can have only one __init__ method, although it can have a lot of
>optional arguments with default values.  What's the correct way to
>deal with a situation like the one I've outlined above?

You can determine the type of the input data by using isinstance() and 
take the appropriate actions depending on this decision:

>>> class MyClass(object):
...     def __init__(self, input_data):
...         if isinstance(input_data, basestring):
...             print "Do actions for string type input"
...         elif isinstance(input_data, float):
...             print "Do actions for float type input"
...     def get_output_data(self):
...         return "output data"
... 
>>> a = MyClass("String")
Do actions for string type input
>>> b = MyClass(15.9)
Do actions for float type input



Best regards,

Günther

[toc] | [prev] | [next] | [standalone]


#18278

FromAdam Funk <a24061@ducksburg.com>
Date2011-12-31 20:55 +0000
Message-ID<de94t8x025.ln2@news.ducksburg.com>
In reply to#18228
On 2011-12-30, Günther Dietrich wrote:

> Adam Funk <a24061@ducksburg.com> wrote:
>
>>Suppose I'm creating a class that represents a bearing or azimuth,
>>created either from a string of traditional bearing notation
>>("N24d30mE") or from a number indicating the angle in degrees as
>>usually measured in trigonometry (65.5, measured counter-clockwise
>>from the x-axis).  The class will have methods to return the same
>>bearing in various formats.
...
> You can determine the type of the input data by using isinstance() and 
> take the appropriate actions depending on this decision:
>
>>>> class MyClass(object):
> ...     def __init__(self, input_data):
> ...         if isinstance(input_data, basestring):
> ...             print "Do actions for string type input"
> ...         elif isinstance(input_data, float):
> ...             print "Do actions for float type input"
> ...     def get_output_data(self):
> ...         return "output data"

Aha, I think I like this approach best, partly because I realized
after writing my post that it might also be good to accept strings
representing "pure" angles (e.g., "65d30m").  So I think I'll use
isinstance *and then* check the input string against some regexes to
determine whether it's in traditional surveying notation or trig
notation in DMS.


-- 
The generation of random numbers is too important to be left to
chance.                                     [Robert R. Coveyou]

[toc] | [prev] | [next] | [standalone]


#18229

FromMel Wilson <mwilson@the-wire.com>
Date2011-12-30 16:08 -0500
Message-ID<jdl990$ika$1@speranza.aioe.org>
In reply to#18226
Adam Funk wrote:

> (Warning: this question obviously reflects the fact that I am more
> accustomed to using Java than Python.)
> 
> Suppose I'm creating a class that represents a bearing or azimuth,
> created either from a string of traditional bearing notation
> ("N24d30mE") or from a number indicating the angle in degrees as
> usually measured in trigonometry (65.5, measured counter-clockwise
> from the x-axis).  The class will have methods to return the same
> bearing in various formats.
> 
> In Java, I would write two constructors, one taking a single String
> argument and one taking a single Double argument.  But in Python, a
> class can have only one __init__ method, although it can have a lot of
> optional arguments with default values.  What's the correct way to
> deal with a situation like the one I've outlined above?

Cleanest from the point of view of the class source code would be factory 
functions at the module level, or special classmethods to deal with the less 
common cases.  You see this a lot in wxPython when they have to deal with 
overloaded C++ constructors.

Most like the Java would be to check within __init__ for a string argument 
that could be parsed as a bearing, and failing that fall back to treating 
the argument as a numeric angle.

Neither fish nor fowl would be to accept named arguments for the different 
kinds of values.

	Mel.

[toc] | [prev] | [next] | [standalone]


#18230

FromArnaud Delobelle <arnodel@gmail.com>
Date2011-12-30 21:14 +0000
Message-ID<mailman.4253.1325279649.27778.python-list@python.org>
In reply to#18226
On 30 December 2011 20:40, Adam Funk <a24061@ducksburg.com> wrote:
> (Warning: this question obviously reflects the fact that I am more
> accustomed to using Java than Python.)
>
> Suppose I'm creating a class that represents a bearing or azimuth,
> created either from a string of traditional bearing notation
> ("N24d30mE") or from a number indicating the angle in degrees as
> usually measured in trigonometry (65.5, measured counter-clockwise
> from the x-axis).  The class will have methods to return the same
> bearing in various formats.
>
> In Java, I would write two constructors, one taking a single String
> argument and one taking a single Double argument.  But in Python, a
> class can have only one __init__ method, although it can have a lot of
> optional arguments with default values.  What's the correct way to
> deal with a situation like the one I've outlined above?

(Using Python 3 below)

Method 1
----------
Your __init__ method could take the angle as an argument (which seems
the most natural to me).  Then you could have a class method that
takes the string

i.e.

class Bearing:
    def __init__(self, angle):
        self.angle = angle
        # or whatever your internal reprsentation is
    @classmethod
    def fromstring(cls, string):
        # Here, work out the angle from the string
        return cls(angle)

So you can do:

b = Bearing(65.5)

or

b = Bearing.fromstring("N24d30mE")

Method 2
----------

You can test the type of the argument of the __init__ method

class Bearing:
    def __init__(self, arg):
        if isinstance(arg, str):
            # here calculate the value of angle
        else:
            angle = float(angle)
        self.angle = angle

Now you can do:

b = Bearing(65.5)

or

b = Bearing("N24d30mE")

Both methods are used for builtin types:

>>> int('12')
12
>>> int(12.5)
12
>>> dict([(1, 2), (3, 4)])
{1: 2, 3: 4}
>>> dict.fromkeys([1, 2])
{1: None, 2: None}

HTH

-- 
Arnaud

[toc] | [prev] | [next] | [standalone]


#18231

FromJason Friedman <jason@powerpull.net>
Date2011-12-30 21:18 +0000
Message-ID<mailman.4254.1325279912.27778.python-list@python.org>
In reply to#18226
> Suppose I'm creating a class that represents a bearing or azimuth,
> created either from a string of traditional bearing notation
> ("N24d30mE") or from a number indicating the angle in degrees as
> usually measured in trigonometry (65.5, measured counter-clockwise
> from the x-axis).  The class will have methods to return the same
> bearing in various formats.
>
> In Java, I would write two constructors, one taking a single String
> argument and one taking a single Double argument.  But in Python, a
> class can have only one __init__ method, although it can have a lot of
> optional arguments with default values.  What's the correct way to
> deal with a situation like the one I've outlined above?

Similar to other answers already posted:


#!/usr/bin/env python
class azimuth:
    def __init__(self, bearing, heading):
        self.bearing = bearing
        self.heading = heading
        if not bearing:
            self.bearing = 30.5 # or, realistically, a calculation
based on the heading
        if not heading:
            self.heading = "N..." # or, realistically, a calculation
based on the bearing
    @staticmethod
    def getBearingInstance(bearing):
        return azimuth(bearing, None)
    @staticmethod
    def getHeadingInstance(heading):
        return azimuth(None, heading)

azimuth1 = azimuth.getBearingInstance("N24d30mE")
print azimuth1.heading

azimuth2 = azimuth.getHeadingInstance(30)
print azimuth2.bearing

[toc] | [prev] | [next] | [standalone]


#18234

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-12-30 22:21 +0000
Message-ID<4efe3955$0$29966$c3e8da3$5496439d@news.astraweb.com>
In reply to#18231
On Fri, 30 Dec 2011 21:18:29 +0000, Jason Friedman wrote:

> class azimuth:
>     def __init__(self, bearing, heading):

It is conventional, and recommended, to use an initial capital letter for 
classes. (Yes, Python built-ins violate that rule, and indeed so do some 
non-built-ins.) See PEP 8 for the recommended style guide.


[...]
>     @staticmethod
>     def getBearingInstance(bearing):
>         return azimuth(bearing, None)
>     @staticmethod
>     def getHeadingInstance(heading):
>         return azimuth(None, heading)

In this case, you should use classmethod rather than staticmethod and 
avoid hard-coding the class:

    @classmethod
    def getBearingInstance(cls, bearing):
        return cls(bearing, None)

That way subclassing will work correctly:


class MyAzimuth(azimuth):
    pass

angle = MyAzimuth.getBearingInstance("N24d30mE")

will return a MyAzimuth instance instead of an azimuth instance.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#18233

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-12-30 22:13 +0000
Message-ID<4efe377b$0$29966$c3e8da3$5496439d@news.astraweb.com>
In reply to#18226
On Fri, 30 Dec 2011 20:40:16 +0000, Adam Funk wrote:

> (Warning: this question obviously reflects the fact that I am more
> accustomed to using Java than Python.)
> 
> Suppose I'm creating a class that represents a bearing or azimuth,
> created either from a string of traditional bearing notation
> ("N24d30mE") or from a number indicating the angle in degrees as usually
> measured in trigonometry (65.5, measured counter-clockwise from the
> x-axis).  The class will have methods to return the same bearing in
> various formats.
> 
> In Java, I would write two constructors, one taking a single String
> argument and one taking a single Double argument.  But in Python, a
> class can have only one __init__ method, although it can have a lot of
> optional arguments with default values.  What's the correct way to deal
> with a situation like the one I've outlined above?

The most idiomatic way to do this would be with named constructor 
functions, or by testing the argument type in __init__. For example:

# Method 1
class Azimuth(object):
    def __init__(self, angle):
        # Initialise an azimuth object from an angle (float)
        self._angle = float(angle)
    @classmethod
    def from_bearing(cls, bearing):
        # Create an azimuth object from a bearing (string).
        angle = cls.bearing_to_angle(bearing)
        return cls(angle)
    @staticmethod
    def bearing_to_angle(bearing):
        # Convert a bearing (string) into a float.
        return 0.0  # whatever...

        
Note some features of this version:

* Normal methods receive the instance as first argument, "self".

* We use the classmethod and staticmethod decorators to create class 
  and static methods. Be warned that the meaning of these are NOT 
  the same as in Java!

* Class methods receive the class object as first argument, "cls". 
  Hence the name. Note that in Python, classes are objects too.

* We make from_bearing a class method, so we can call it from either
  the class itself:

      ang = Azimuth.from_bearing("25N14E")

  or from an existing instance:

      ang2 = ang.from_bearing("17N31W")

* Static methods don't receive either the class or the instance. They
  are equivalent to a top level function, except encapsulated inside
  a class.


# Method 2
class Azimuth(object):
    def __init__(self, arg):
        # Initialise an azimuth object from arg, either an angle (float)
        # or a bearing (string).
        if isinstance(arg, str):
            angle = bearing_to_angle(arg)
        else:
            angle = float(arg)
        self._angle = float(angle)

def bearing_to_angle(bearing):
    # Convert a bearing (string) into a float.
    return 0.0  # whatever...


Note that in this example, I've turned bearing_to_angle into a regular 
function outside of the class instead of a static method. Just because I 
can. This is probably slightly more idiomatic than the use of static 
methods.


Either method is acceptable, although the first is slightly more "pure" 
because it doesn't use isinstance. The second may fail if the user passes 
a string-like object which behaves identically to strings, but doesn't 
inherit from str. If you care about that, you should prefer the first way 
with an explicit from_bearing method.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#18235

FromRoy Smith <roy@panix.com>
Date2011-12-30 18:24 -0500
Message-ID<roy-C73AC0.18242330122011@news.panix.com>
In reply to#18226
In article <g6k1t8xg0a.ln2@news.ducksburg.com>,
 Adam Funk <a24061@ducksburg.com> wrote:

> (Warning: this question obviously reflects the fact that I am more
> accustomed to using Java than Python.)
> 
> Suppose I'm creating a class that represents a bearing or azimuth,
> created either from a string of traditional bearing notation
> ("N24d30mE") or from a number indicating the angle in degrees as
> usually measured in trigonometry (65.5, measured counter-clockwise
> from the x-axis).

There's two ways to do this.

One would be to have the __init__ method switch on the type of its 
argument:

def __init__(self, bearing_or_azimuth):
   if isinstance(bearing_or_azimuth, basestring):
      # do the bearing thing
   else:
      # do the azimuth thing

I suspect many people would consider that unpythonic.  The other way 
would be what, in the C++/Java world, would be called the "named 
constructor idiom".  Just write two factory functions:

class DirectionIndicatingThingie:
   @staticmethod
   def from_bearing(cls, bearing):
      dit = DirectionIndicatingThingie()
      dit.direction = whatever
      return dit

and likewise for from_azimuth()

"But!", some C++/Java type bondage addicts might cry, "there's nothing 
to prevent somebody from creating a DirectionIndicatingThingie directly, 
bypassing the factory functions.  There's no way to make the constructor 
private!".  To which the free-willed pythonistas would respond, "If it 
hurts when you do that, don't do that".

[toc] | [prev] | [next] | [standalone]


#18236

FromChris Angelico <rosuav@gmail.com>
Date2011-12-31 10:36 +1100
Message-ID<mailman.4256.1325288188.27778.python-list@python.org>
In reply to#18235
On Sat, Dec 31, 2011 at 10:24 AM, Roy Smith <roy@panix.com> wrote:
> "But!", some C++/Java type bondage addicts might cry, "there's nothing
> to prevent somebody from creating a DirectionIndicatingThingie directly,
> bypassing the factory functions.  There's no way to make the constructor
> private!".  To which the free-willed pythonistas would respond, "If it
> hurts when you do that, don't do that".

You know a Python programmer's been at your C++ code when it opens:
#define class struct

ChrisA

[toc] | [prev] | [next] | [standalone]


#18237

FromRoy Smith <roy@panix.com>
Date2011-12-30 18:39 -0500
Message-ID<roy-06A099.18391630122011@news.panix.com>
In reply to#18236
In article <mailman.4256.1325288188.27778.python-list@python.org>,
 Chris Angelico <rosuav@gmail.com> wrote:

> On Sat, Dec 31, 2011 at 10:24 AM, Roy Smith <roy@panix.com> wrote:
> > "But!", some C++/Java type bondage addicts might cry, "there's nothing
> > to prevent somebody from creating a DirectionIndicatingThingie directly,
> > bypassing the factory functions.  There's no way to make the constructor
> > private!".  To which the free-willed pythonistas would respond, "If it
> > hurts when you do that, don't do that".
> 
> You know a Python programmer's been at your C++ code when it opens:
> #define class struct

Why stop there?

#define private public

[toc] | [prev] | [next] | [standalone]


#18238

FromChris Angelico <rosuav@gmail.com>
Date2011-12-31 10:47 +1100
Message-ID<mailman.4257.1325288842.27778.python-list@python.org>
In reply to#18237
On Sat, Dec 31, 2011 at 10:39 AM, Roy Smith <roy@panix.com> wrote:
> In article <mailman.4256.1325288188.27778.python-list@python.org>,
>  Chris Angelico <rosuav@gmail.com> wrote:
>
>> You know a Python programmer's been at your C++ code when it opens:
>> #define class struct
>
> Why stop there?
>
> #define private public

Probably yeah, do both. Anyway, life's so much easier when you don't
have to write trivial getter/setter methods (and then maintain them).
I've never had a situation where I've changed a private member while
keeping the getters and setters unchanged; the ONLY benefit accessor
methods have ever given to me personally has been logging (and
granted, that is hard to do without them - since you can't override
__getitem__ in C++ - but how often do you really need that facility?).

I used to believe in the separation of interface from implementation.
Then I realised that most of the separation was transparent anyway,
and gave up on it. And then realised why the separation is a good idea
after all. :)

ChrisA

[toc] | [prev] | [next] | [standalone]


#18279

FromAdam Funk <a24061@ducksburg.com>
Date2011-12-31 20:59 +0000
Message-ID<5m94t8x025.ln2@news.ducksburg.com>
In reply to#18235
On 2011-12-30, Roy Smith wrote:

> "But!", some C++/Java type bondage addicts might cry, "there's nothing 
> to prevent somebody from creating a DirectionIndicatingThingie directly, 
> bypassing the factory functions.  There's no way to make the constructor 
> private!".  To which the free-willed pythonistas would respond, "If it 
> hurts when you do that, don't do that".

Actually one problem that can occur in large Java projects is that the
package structure requires some things to have public constructors
(even when you'd rather not do that) so the Factory class in the main
package has access to them.


-- 
English has perfect phonetic spelling. It just doesn't have phonetic
pronunciation.                                        [Peter Moylan]

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web