Idea about method parameters

Kragen Sitaker kragen at dnaco.net
Tue Oct 16 21:00:00 EDT 2001


In article <1634093.6GMIcojqen at lunix.schabi.de>,
Markus Schaber  <markus at schabi.de> wrote:
>Well - compare the following example just reconstructed from 
>a class I use in a small database, building a chained list:
>
>class Tankung:
>  def __init__(Liter, # the amount of fuel
>               Datum, # the date
>               Preis, # the price
>               Tachostand, # the kilometer count
>               Waehrung = "DM", # DM or Euro?
>               Tankstelle, # where was it bought
>               Bemerkung= "", # any remarks?
>               prev = None) # the previous one, omit for first in chain
>
>    self.Liter = Liter # set the amount of fuel
>    self.Datum = Datum # set the date
>    self.Preis = Preis # set the price
>    self.Waehrung = Waehrung # set the currency
>    self.Tankstelle = Tankstelle # set the fuel station
>    self.Bemerkung = Bemerkung # set the remarks
>
>    self.Tachostand = int(Tachostand) # ensure the kilometer count is
>                                      # an integer
>
>    self.prev = prev # set the previous one
>    try: # build the chained list
>      self.prev.next = self
>    except TypeError:
>      pass #prev was None or alike
>
>and in the proposed syntax:
>
>class Tankung:
>  def __init__(self.Liter, # set the fuel
>               self.Datum, # set the date
>               self.Preis, # set the price
>               Tachostand, # the kilometer count
>               self.Waehrung = "DM", # DM or Euro?
>               self.Tankstelle, # where was it bought
>               self.Bemerkung= "", # any remarks?
>               self.prev = None) # the previous one, omit for first in
>                                 # chain
>    
>    self.Tachostand = int(Tachostand) # ensure the kilometer count is
>                                      # an integer
>    try: # build the chained list
>      self.prev.next = self
>    except TypeError:
>      pass #prev was None or alike
>
>I just rebuilt it from brain because the source is on another machine, 
>and put the Comments in English instead of German so there might be 
>typos :-)
>
>In my eyes, the second example is more clear and elegant.

I agree; you're already asking for trouble by duplicating the long list
of attribute list and in the actual parameters where Tankungen are
created, but if you create Tankungen in a lot of places, the
readability might be worth it.

Coincidentally, I wrote something this morning that might make your
life easier, and posted it under the subject 'defstruct.py'.

# I occasionally find myself writing code like the following:
# class Point:
#     def __init__(self, x, y):
#         self.x = x
#         self.y = y
# and that's the whole class.  This little module lets me write the above code
# as
# Point = defstruct('x', 'y')
# and have done with it.
# 
# The name is taken from Common Lisp; the syntax is taken from MzScheme's
# define-struct and is similar to the Common Lisp boa constructor syntax.
# define-record-type from SRFI 9 is nasty and Scheme-specific enough that
# I didn't use it.
# 
# I hereby dedicate this code to the public domain and disavow any copyright
# interest in it.
# 
# -- Kragen Sitaker, 2001-10-16

def defstruct(*fields):
    class Struct:
        def __init__(self, *contents):
            if len(contents) != len(self.structfields):
                raise TypeError, (
                    "wrong number of arguments: expected %d %s, got %d" %
                    (len(self.structfields),
                     repr(self.structfields),
                     len(contents)))
            for fieldnum in range(len(contents)):
                setattr(self, self.structfields[fieldnum], contents[fieldnum])
    Struct.structfields = fields
    return Struct    
        
def test():
    point = defstruct('x', 'y')
    p1 = point(1, 2)
    assert p1.x == 1
    assert p1.y == 2
    complex = defstruct('real', 'imag')
    assert point is not complex
    assert isinstance(p1, point)
    assert not isinstance(p1, complex)

test()

With this, your code above would become something like:

Now, as it sits, it doesn't support default or named parameters; but
those wouldn't be hard to add.  Your example becomes:

from defstruct import defstruct

Tankung = defstruct('Liter', 'Datum', 'Preis', 'Tachostand', 'Waehrung',
                    'Tankstelle', 'Bemerkung')
class Listnode:
    def __init__(self, data, prev=None):
        self.tank = apply(Tankung, data)
        self.prev = prev
        if self.prev is not None:
            self.prev.next = self

Me, I'd probably dispense with the Listnode and just make Python lists
of Tankungen, but I assume you tried that first and it wasn't good
enough.

I think I could add parameters with defaults as follows:

Tankung = defstruct('Liter', 'Datum', 'Preis', 'Tachostand', 
                    {'Waehrung': "DM"}, {'Tankstelle': "nowhere"}, 
                    {'Bemerkung': ""})

That's not as pretty as Waehrung="DM", Tankstelle="nowhere",
Bemerkung="", but that's the price we pay for having a non-extensible
language syntax.  (If we ask for all the keyword arguments inside
defstruct, we get them, in a dict.  Shuffled into an arbitrary order.
Oops.)

And I think I could just throw any and all named parameters (whether or
not mentioned in the original struct definition) into the object.

None of the above requires any changes to the language.
-- 
<kragen at pobox.com>       Kragen Sitaker     <http://www.pobox.com/~kragen/>
Perilous to all of us are the devices of an art deeper than we possess
ourselves.
       -- Gandalf the White [J.R.R. Tolkien, "The Two Towers", Bk 3, Ch. XI]




More information about the Python-list mailing list