Concrete classes -- stylistic question
Kragen Sitaker
kragen at pobox.com
Sun Oct 13 13:35:54 EDT 2002
Andrew Koenig <ark at research.att.com> writes:
> ...
> Of course, I could define little classes like this:
> class xy(object):
> def __init__(self, x, y):
> self.x, self.y = x, y
> ...
> If I want a number of such classes, it's something of a pain to define
> each one separately. So I came up with an idea:
> ...
> def __init__(self, **args):
> for i in args:
> setattr(self, i, args[i])
MetaPy.defrecord implements something similar, for similar reasons; it
even includes some syntactic sugar (semantic sugar?) to make it easy
to incrementally change from using tuples to using
MetaPy.defrecord.records.
>>> from MetaPy.defrecord import defrecord
>>> xy = defrecord('x', 'y')
>>> p = xy(3.4, 5.2)
>>> p
MetaPy.defrecord.record(x=3.3999999999999999, y=5.2000000000000002)
>>> x, y = p
>>> print x, y
3.4 5.2
And it supports named arguments like the ones you used above as well:
>>> p2 = xy(x = 3, y = 2, z = 5)
>>> p3 = xy(x = 3, z = 5)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/usr/local/lib/python2.1/site-packages/MetaPy/defrecord.py", line 87, in __init__
raise TypeError, (("takes exactly %d args " +
TypeError: takes exactly 2 args (1 given, first missing arg is y)
>>> p2
MetaPy.defrecord.record(x=3, y=2, z=5)
MetaPy is at http://pobox.com/~kragen/sw/MetaPy-7.tar.gz and
http://pobox.com/~kragen/sw/MetaPy-7.exe, works in Python 1.5.2 and
up, and is free software licensed under the GNU GPL. The
implementation of defrecord is as follows:
def defrecord(*args, **kwargs):
class Record:
def __init__(self, *args, **kwargs):
if len(args) > len(self.defrecord_tuplevals):
raise TypeError, ("takes exactly %d args, given %d" %
(len(self.defrecord_tuplevals), len(args)))
for ii in range(len(self.defrecord_tuplevals)):
attr = self.defrecord_tuplevals[ii]
if ii < len(args):
if kwargs.has_key(attr):
raise TypeError, (("multiple values for " +
"keyword argument '%s'") %
attr)
setattr(self, attr, args[ii])
elif (not kwargs.has_key(attr) and not hasattr(self, attr)):
# I wish this error message were more accurate
raise TypeError, (("takes exactly %d args " +
"(%d given, first missing arg is %s)")
% (len(self.defrecord_tuplevals),
ii, self.defrecord_tuplevals[ii]))
for key, value in kwargs.items():
setattr(self, key, value)
def __getitem__(self, ii):
if self.defrecord_allow_getitem:
return getattr(self, self.defrecord_tuplevals[ii])
else:
raise TypeError, "unsubscriptable object"
def __len__(self):
if self.defrecord_allow_getitem:
return len(self.defrecord_tuplevals)
else:
raise TypeError, "len() of unsized object"
# this isn't quite right, unfortunately.
# evaluating this expression (assuming it's legal Python, which
# it only will be if people choose attribute names that are
# identifiers) will return an object with the same attributes,
# but not the same __getitem__ or class.
defrecord_classname = 'MetaPy.defrecord.record'
def __repr__(self):
rv = []
for attr in dir(self):
rv.append("%s=%s" % (attr, repr(getattr(self, attr))))
return (self.defrecord_classname + '(' +
string.join(rv, ", ") + ")")
if kwargs.has_key('allow_getitem'):
Record.defrecord_allow_getitem = kwargs['allow_getitem']
else:
Record.defrecord_allow_getitem = 1
if kwargs.has_key('name'): Record.defrecord_classname = kwargs['name']
Record.defrecord_tuplevals = args
return Record
More information about the Python-list
mailing list