Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

structs in python

7 views
Skip to first unread message

Kevin O'Connor

unread,
Jul 6, 2002, 11:12:00 PM7/6/02
to
Hello,

I often find myself using Python tuples in the same way I might use C
structs. For example, I might store a "point" as "p = (x, y, color)".

Unfortunately, this quickly becomes cumbersome when the code starts to make
frequent references to tuple positions instead of member names. For
example, one would see code like "delta = (p1[0] - p2[0], p1[1] - p2[1])".
Ideally, this code would read something more like "delta = (p1.x - p2.x,
p1.y - p2.y)".

Clearly, the use of classes makes the code much more readable, but
unfortunately the declarations necessary to instantiate a class is often
too much of a hassle for small and infrequently used objects.

It would be useful if there were a simple way of declaring a class with
only member variables (and no methods); an object more akin to a C struct.

What if a syntax like the following were permitted:
>>> p = ( .x = 10, .y = 11, .color = 'blue')
>>> print p.x
10
>>>

- a mix between python tuples and C99 struct initializers. This type of
declaration would provide a convenient way of naming the parts of small
stand-alone objects - the objects that typically use tuples today.

I know this concept is not unique - I've seen implementations of a "class
Struct" that implements the above using Python's **kw syntax (Eg. "p =
Struct(x=10, y=11, color='blue')" ). However, I have not seen wide spread
adoption of it, and I have not seen an attempt to standardize an
implementation.

My intention here is not really to propose any changes, but instead to
query the community's feelings on this topic. Do other people find code
that uses tuples a bit cryptic? Would others like a named initializer? Is
this a recurring request with obvious problems?

Thanks,
-Kevin

--
------------------------------------------------------------------------
| Kevin O'Connor "BTW, IMHO we need a FAQ for |
| ke...@koconnor.net 'IMHO', 'FAQ', 'BTW', etc. !" |
------------------------------------------------------------------------


Paul Rubin

unread,
Jul 6, 2002, 11:46:28 PM7/6/02
to
"Kevin O'Connor" <ke...@koconnor.net> writes:
> Unfortunately, this quickly becomes cumbersome when the code starts to make
> frequent references to tuple positions instead of member names. For
> example, one would see code like "delta = (p1[0] - p2[0], p1[1] - p2[1])".
> Ideally, this code would read something more like "delta = (p1.x - p2.x,
> p1.y - p2.y)".
>
> Clearly, the use of classes makes the code much more readable, but
> unfortunately the declarations necessary to instantiate a class is often
> too much of a hassle for small and infrequently used objects.

It doesn't take much:

class frob: pass

p1 = frob()
p1.x = 3
p1.y = 5

etc. That's what I usually do.

Erik Max Francis

unread,
Jul 7, 2002, 12:30:19 AM7/7/02
to
Kevin O'Connor wrote:

> What if a syntax like the following were permitted:
> >>> p = ( .x = 10, .y = 11, .color = 'blue')
> >>> print p.x
> 10

This kind of thing is why you have slots. Check the documentation for
the __slots__ class attribute.

--
Erik Max Francis / m...@alcyone.com / http://www.alcyone.com/max/
__ San Jose, CA, US / 37 20 N 121 53 W / ICQ16063900 / &tSftDotIotE
/ \ See the son in your bad day / Smell the flowers in the valley
\__/ Chante Moore
Bosskey.net: Aliens vs. Predator 2 / http://www.bosskey.net/avp2/
A personal guide to Aliens vs. Predator 2.

Sean 'Shaleh' Perry

unread,
Jul 7, 2002, 12:26:00 AM7/7/02
to

On 07-Jul-2002 Kevin O'Connor wrote:
> Hello,
>
> I often find myself using Python tuples in the same way I might use C
> structs. For example, I might store a "point" as "p = (x, y, color)".
>
> Unfortunately, this quickly becomes cumbersome when the code starts to make
> frequent references to tuple positions instead of member names. For
> example, one would see code like "delta = (p1[0] - p2[0], p1[1] - p2[1])".
> Ideally, this code would read something more like "delta = (p1.x - p2.x,
> p1.y - p2.y)".
>
> Clearly, the use of classes makes the code much more readable, but
> unfortunately the declarations necessary to instantiate a class is often
> too much of a hassle for small and infrequently used objects.
>
> It would be useful if there were a simple way of declaring a class with
> only member variables (and no methods); an object more akin to a C struct.
>

>>> class Point:
... pass
...
>>> p = Point()
>>> p.x = 1
>>> p.y = 2
>>> p.color = "blue"
>>> p2 = Point()
>>> p2.x = 3
>>> p2.y = 5
>>> p2.color = "green"

However this breaks down for your delta = ... example.

is

class Point:
def __init__(x, y, color):
self.x = x
self.y = y
self.color = color

really that much to type?

delta = Point(p2.x - p.x, p2.y - p.y, default_color)

Paul Rubin

unread,
Jul 7, 2002, 2:30:25 AM7/7/02
to
"Sean 'Shaleh' Perry" <shale...@attbi.com> writes:
> However this breaks down for your delta = ... example.
>
> is
>
> class Point:
> def __init__(x, y, color):
> self.x = x
> self.y = y
> self.color = color
>
> really that much to type?

Yes, it seems clumsy to me. See how error prone it is: your __init__
function arg list forgot to mention 'self'.

The new slots scheme may be better but it doesn't exist in older
Pythons and isn't well documented in 2.2.

Opus

unread,
Jul 7, 2002, 6:58:13 AM7/7/02
to
And the __slots__ way is strange if you are using the __variables in your
class.

class thing(object):
__slots__ = ["_thing__var"] # Huh???
def __init__(self,init):
self.__var = init

For the point idea, the typo there would have been caught in the compile time.
And, doing the delta actually makes much more sense overloading __sub__ for the
point class.

class Point(object):
def __init__(self,x,y,color):
self.x=x
self.y=y
self.color = color
def __sub__(self,other):
return Point(self.X-other.X, self.Y-other.Y, self.color-other.color)

True, this is some code to create, but because it is something that is not
going to be used that often, it means that you control the way it is done in
one place and you don't have to remember, or research how to do it everytime
you use it. And, yes, I have used classes in C++ as structs, and I remember
that it was the prefered way to handle structs.

> --
> http://mail.python.org/mailman/listinfo/python-list


--Opus--

Eagles may soar, but weasels don't get sucked into jet engines.
- Unknown

--------------------------------------------------------
Get added to my Humor list:
mailto:op...@value.net?subject=ADD_HUMOR
Get added to my Neat list:
mailto:op...@value.net?subject=ADD_NEAT
Get my PGP public key:
mailto:op...@value.net?subject=PSEND&body=send%20PublicKEY.asc
Visit My Home Page:
http://value.net/~opus/

Alex Martelli

unread,
Jul 7, 2002, 7:45:45 AM7/7/02
to
Kevin O'Connor wrote:

> Hello,
>
> I often find myself using Python tuples in the same way I might use C
> structs. For example, I might store a "point" as "p = (x, y, color)".
>
> Unfortunately, this quickly becomes cumbersome when the code starts to
> make
> frequent references to tuple positions instead of member names. For
> example, one would see code like "delta = (p1[0] - p2[0], p1[1] - p2[1])".
> Ideally, this code would read something more like "delta = (p1.x - p2.x,
> p1.y - p2.y)".
>
> Clearly, the use of classes makes the code much more readable, but
> unfortunately the declarations necessary to instantiate a class is often
> too much of a hassle for small and infrequently used objects.
>
> It would be useful if there were a simple way of declaring a class with
> only member variables (and no methods); an object more akin to a C struct.
>
> What if a syntax like the following were permitted:
>>>> p = ( .x = 10, .y = 11, .color = 'blue')
>>>> print p.x
> 10
>>>>

I think you're looking for the recipe which I described in the Cookbook
as Bunch.

> I know this concept is not unique - I've seen implementations of a "class
> Struct" that implements the above using Python's **kw syntax (Eg. "p =
> Struct(x=10, y=11, color='blue')" ). However, I have not seen wide spread
> adoption of it, and I have not seen an attempt to standardize an
> implementation.

What sort of implementation standardization might possibly be necessary,
or even helpful?

class Bunch:
def __init__(self, **kw): self.__dict__ = kw

How can you possibly get simpler than this?

As for "widespread adoption", I guess the occurrence of classes with no
behavior, just state, isn't all that frequent when one does OOP. It
does happen, and when it happens Bunch is fine -- as long as issues of
amount of memory consumed don't interfere. __slots__ is OK, to save
memory, when you have many instances with just a few data fields each
(and the same field names in each instance), but it does admittedly
demand more sophisticated implementation than Bunch, presumably via a
metaclass.


> My intention here is not really to propose any changes, but instead to
> query the community's feelings on this topic. Do other people find code
> that uses tuples a bit cryptic? Would others like a named initializer?
> Is this a recurring request with obvious problems?

I just don't see what the lack of classname and extra leading dots in your
proposed :

>>>> p = ( .x = 10, .y = 11, .color = 'blue')

add to current usage such as p = Bunch(x=10, y=11, color='blue'), I guess.


Alex

Jesper Olsen

unread,
Jul 7, 2002, 7:50:03 AM7/7/02
to
Paul Rubin <phr-n...@NOSPAMnightsong.com> wrote in message news:<7xsn2wy...@ruckus.brouhaha.com>...

>
> Yes, it seems clumsy to me. See how error prone it is: your __init__
> function arg list forgot to mention 'self'.

Exactely! Why self?
This part of the python syntax is clumsy :-)

Jesper

Alex Martelli

unread,
Jul 7, 2002, 11:24:44 AM7/7/02
to
Opus wrote:

> And the __slots__ way is strange if you are using the __variables in your
> class.
>
> class thing(object):
> __slots__ = ["_thing__var"] # Huh???
> def __init__(self,init):
> self.__var = init

This is a bug in Python 2.2, already fixed in the Python 2.3 pre-alpha
that's currently in the CVS tree -- now, type.__new__ properly 'decorates'
(aka 'mangles') each slotname whose name starts with '__' (and does not
end with '__') so __slots__=['__var'] will work fine here. Hopefully
this bug fix will be backported to 2.2.2 ...


Alex

Alex Martelli

unread,
Jul 7, 2002, 11:49:16 AM7/7/02
to
Alex Martelli wrote:
...

> amount of memory consumed don't interfere. __slots__ is OK, to save
> memory, when you have many instances with just a few data fields each
> (and the same field names in each instance), but it does admittedly
> demand more sophisticated implementation than Bunch, presumably via a
> metaclass.

Ah, forget the "presumably", here's a simple example. I've been looking
for good and simple custom metaclass examples for quite a while, after
all, so it would be silly to miss this occasion just because I SHOULD be
finishing the draft of Python in a Nutshell...!-).


import warnings

class metaMetaBunch(type):
"""
metaclass for new and improved "Bunch": implicitly defines
__slots__, __init__ and __repr__ from variables bound in class scope.

An instance of metaMetaBunch (a class whose metaclass is metaMetaBunch)
defines only class-scope variables (and possibly special methods, but
NOT __init__ and __repr__!). metaMetaBunch removes those variables from
class scope, snuggles them instead as items in a class-scope dict named
__dflts__, and puts in the class a __slots__ listing those variables'
names, an __init__ that takes as optional keyword arguments each of
them (using the values in __dflts__ as defaults for missing ones), and
a __repr__ that shows the repr of each attribute that differs from its
default value (the output of __repr__ can be passed to __eval__ to make
an equal instance, as per the usual convention in the matter).
"""

def __new__(cls, classname, bases, classdict):
""" Everything needs to be done in __new__, since type.__new__ is
where __slots__ are taken into account.
"""

# define as local functions the __init__ and __repr__ that we'll
# use in the new class

def __init__(self, **kw):
""" Simplistic __init__: first set all attributes to default
values, then override those explicitly passed in kw.
"""
for k in self.__dflts__: setattr(self, k, self.__dflts__[k])
for k in kw: setattr(self, k, kw[k])

def __repr__(self):
""" Clever __repr__: show only attributes that differ from the
respective default values, for compactness.
"""
rep = [ '%s=%r' % (k, getattr(self, k)) for k in self.__dflts__
if getattr(self, k) != self.__dflts__[k]
]
return '%s(%s)' % (classname, ', '.join(rep))

# build the newdict that we'll use as class-dict for the new class
newdict = { '__slots__':[], '__dflts__':{},
'__init__':__init__, '__repr__':__repr__, }

for k in classdict:
if k.startswith('__'):
# special methods &c: copy to newdict, warn about conflicts
if k in newdict:
warnings.warn("Can't set attr %r in bunch-class %r" % (
k, classname))
else:
newdict[k] = classdict[k]
else:
# class variables, store name in __slots__ and name and
# value as an item in __dflts__
newdict['__slots__'].append(k)
newdict['__dflts__'][k] = classdict[k]

# finally delegate the rest of the work to type.__new__
return type.__new__(cls, classname, bases, newdict)


class MetaBunch(object):
""" For convenience: inheriting from MetaBunch can be used to get
the new metaclass (same as defining __metaclass__ yourself).
"""
__metaclass__ = metaMetaBunch


# Example use: a meta-bunch class
class Point(MetaBunch):
""" A point has x and y coordinates, defaulting to 0.0, and a color,
defaulting to 'gray' -- and nothing more, except what Python and
the metaclass conspire to add, such as __init__ and __repr__
"""
x = 0.0
y = 0.0
color = 'gray'

# example uses of class Point
q = Point()
print q

p = Point(x=1.2, y=3.4)
print p


Apart from arbitrary ordering of the arguments, and representation of
floating point numbers being not all that nice, you'll see the "print"
statements emit basically the same constructors we just used.


The point is: if you use a lot of "struct-like" classes, metaMetaBunch
offers you a nice and convenient way to define them, with clean syntax
and reasonable default semantics (__init__ and __repr__). If your
application needs are slightly different, you can of course tweak this
pretty easily -- it IS, after all, a modest amount of pretty simple
lines of code. An instance of a metabunch class is quite memory-lean,
and using it should be just about as fast as using an instance of the
original Bunch class. I hope this provides a reasonable starting point
in the study of "what are Python 2.2 metaclasses good for"!

BTW, note that this example doesn't care about the bugfix in the
current CVS 2.3 wrt 2.2 regarding the mangling of slotnames that start
with '__' -- the metabunch class we build doesn't end up with any slotname
with two leading underscores, anyway, and if we have something like:

class thing(MetaBunch):
__var = 23

the class-scope variable name is already mangled to _thing__var by
the time our metaMetaBunch class gets to deal with it. Of course,
this doesn't necessarily make much sense for a class meant to have
no methods (!), but, hey, whatever floats your boat...


Alex


Christopher A. Craig

unread,
Jul 8, 2002, 1:22:21 AM7/8/02
to
"Kevin O'Connor" <ke...@koconnor.net> writes:

> What if a syntax like the following were permitted:
> >>> p = ( .x = 10, .y = 11, .color = 'blue')
> >>> print p.x
> 10
> >>>

Why don't you just use a dict?

>>> p = {'x'=10, 'y'=11, 'color'=blue}
>>> print p['x']
10
>>>

--
Christopher A. Craig <list-...@ccraig.org>
"Going to school make a person educated, any more than going to a
garage makes a person a car" Slashdot


Gerhard Häring

unread,
Jul 8, 2002, 2:09:20 AM7/8/02
to
* Christopher A. Craig <list-...@ccraig.org> [2002-07-08 01:22 -0400]:

> "Kevin O'Connor" <ke...@koconnor.net> writes:
>
> > What if a syntax like the following were permitted:
> > >>> p = ( .x = 10, .y = 11, .color = 'blue')
> > >>> print p.x
> > 10
> > >>>
>
> Why don't you just use a dict?
>
> >>> p = {'x'=10, 'y'=11, 'color'=blue}
> >>> print p['x']
> 10
> >>>

I prefer tiny data-only classes. These can be used exactly like a a
struct.

Gerhard
--
mail: gerhard <at> bigfoot <dot> de registered Linux user #64239
web: http://www.cs.fhm.edu/~ifw00065/ OpenPGP public key id AD24C930
public key fingerprint: 3FCC 8700 3012 0A9E B0C9 3667 814B 9CAA AD24 C930
reduce(lambda x,y:x+y,map(lambda x:chr(ord(x)^42),tuple('zS^BED\nX_FOY\x0b')))


Paul Rubin

unread,
Jul 8, 2002, 2:29:28 AM7/8/02
to
list-...@ccraig.org (Christopher A. Craig) writes:
> Why don't you just use a dict?
>
> >>> p = {'x'=10, 'y'=11, 'color'=blue}
> >>> print p['x']
> 10

Typing p['x'] is more cumbersome than typing p.x and doesn't convey
the same type of meaning. p.x sounds more like it refers to a fixed
slot in a structure.

Christophe Delord

unread,
Jul 8, 2002, 1:29:34 PM7/8/02
to

The dict can be the __dict__ attribute of a particular class :


class Record:

def __init__(self, **kw):
self.__dict__.update(kw);

p = Record(x=10, y=11, color='blue')

print p.x

On 07 Jul 2002 23:29:28 -0700
Paul Rubin <phr-n...@NOSPAMnightsong.com> wrote:


--

(o_ Christophe Delord _o)
//\ http://christophe.delord.free.fr/ /\\
V_/_ mailto:christop...@free.fr _\_V

Fernando Pérez

unread,
Jul 8, 2002, 2:51:47 PM7/8/02
to
Christophe Delord wrote:

>
> The dict can be the __dict__ attribute of a particular class :
>
>
> class Record:
>
> def __init__(self, **kw):
> self.__dict__.update(kw);
>
> p = Record(x=10, y=11, color='blue')
>
> print p.x

such a class can then be conveniently updated later with the following:

def with(object, **args):
"""Set multiple attributes for an object, similar to Pascal's with.

Example:
with(jim,
born = 1960,
haircolour = 'Brown',
eyecolour = 'Green')

Credit: Greg Ewing, in
http://mail.python.org/pipermail/python-list/2001-May/040703.html"""

object.__dict__.update(args)


If you want a full-blown class which implements struct-like functionality
with both dictionary-type (s['blah']) and struct-type (s.blah) access for its
data members, you can use my Struct.py from:
http://windom.colorado.edu/~fperez/python/misc/Struct.py
An html syntax-highligted version is at:
http://windom.colorado.edu/~fperez/python/misc/Struct.py.html

Having both kinds of access allows explicit named acess:
s['blah'] == s.blah
but also access by variable:
x='blah'
s[x] == s['blah']

Obviously, any '.' access is explicit by name always.

Besides allowing both types of access (dict and struct), it has all the
behavior of a dict (keys, values, items, update, get, setdefault, etc) plus a
fancy 'merge' method which is a flexible version of update. It allows you to
update a Struct with fine control over what to do when collisions are found
(update simply overwrites old data, not always what you want). This makes it
very useful for keeping configuration information which needs to be updated
in various forms, for example.

I've successfully used this class in several projects, and it's extremely
convenient. Note however that it was coded for convenience, not speed. If you
use it and find any bugs in it, please let me know.


Note that it requires the following function from a module I have around
called genutils. You can simply paste the following in Struct.py and remove
the import line:

def list2dict2(lst,default=''):
"""Takes a list and turns it into a dict.
Much slower than list2dict, but more versatile. This version can take
lists with sublists of arbitrary length (including sclars)."""

dic = {}
for elem in lst:
if type(elem) in (types.ListType,types.TupleType):
size = len(elem)
if size == 0:
pass
elif size == 1:
dic[elem] = default
else:
k,v = elem[0], elem[1:]
if len(v) == 1: v = v[0]
dic[k] = v
else:
dic[elem] = default
return dic

Paul Magwene

unread,
Jul 13, 2002, 5:27:21 PM7/13/02
to
On Sun, 07 Jul 2002 00:12:00 -0400, Kevin O'Connor wrote:

> It would be useful if there were a simple way of declaring a class with
> only member variables (and no methods); an object more akin to a C
> struct.
>
> What if a syntax like the following were permitted:
>>>> p = ( .x = 10, .y = 11, .color = 'blue')

I often find myself doing something similar to the following (usually
when I want to return multiple things from a function)

>>> class Struct:
... pass
...
>>> p = Struct()
>>> p.x = 10
>>> p.y = 11
>>> p.color = 'blue'

That's slightly more verbose than you're proposed syntax but doesn't
usually feel to onerous (especially since you only have to declare the
Struct class once). If you like everything on one line you could do:

>>> p = Struct(); p.x = 10; p.y = 11; p.color = 'blue'


--Paul

Gregor Lingl

unread,
Jul 13, 2002, 8:02:49 PM7/13/02
to

"Paul Magwene" <p.ma...@snet.net> wrote in message
news:pan.2002.07.13.21...@snet.net...

> On Sun, 07 Jul 2002 00:12:00 -0400, Kevin O'Connor wrote:
>
>
>
> > It would be useful if there were a simple way of declaring a class with
> > only member variables (and no methods); an object more akin to a C
> > struct.
> >
> > What if a syntax like the following were permitted:
> >>>> p = ( .x = 10, .y = 11, .color = 'blue')
>

How do you like this one:

>>> class Struct:
def __init__(self, **args):
self.__dict__=args


>>> p = Struct(x=10,y=11,color='blue')
>>> p.x
10
>>> p.y
11
>>> p.color
'blue'
>>> p.__dict__
{'color': 'blue', 'y': 11, 'x': 10}
>>> p.color='red'
>>> p.__dict__
{'color': 'red', 'y': 11, 'x': 10}
>>> p.color
'red'
>>>


Fredrik Lundh

unread,
Jul 14, 2002, 6:53:05 AM7/14/02
to
Paul Magwene wrote:

> > What if a syntax like the following were permitted:
> >>>> p = ( .x = 10, .y = 11, .color = 'blue')
>
> I often find myself doing something similar to the following (usually
> when I want to return multiple things from a function)
>
> >>> class Struct:
> ... pass
> ...
> >>> p = Struct()
> >>> p.x = 10
> >>> p.y = 11
> >>> p.color = 'blue'
>
> That's slightly more verbose than you're proposed syntax but doesn't
> usually feel to onerous (especially since you only have to declare the
> Struct class once).

the more reason to "do the right thing", right? ;-)

class Struct:
def __init__(self, **kw):
self.__dict__.update(kw)

> If you like everything on one line you could do:
>
> >>> p = Struct(); p.x = 10; p.y = 11; p.color = 'blue'

p = Struct(x=10, y=11, color='blue')

</F>


0 new messages