On 13/08/2016 17:06, Steven D'Aprano wrote:
On Sat, 13 Aug 2016 08:09 pm, BartC wrote:
record date= # define the record
var day, month, year
end
d := date(25,12,2015) # create an instance
Sure. But you've put most of the work into the compiler.
Into the language so that it's always available and consistent rather
than leaving it to individuals to somehow build something on top of
existing language features.
But out of
curiosity, I thought I'd write something to define a record as you would
have it.
A couple of tricky bits:
- Since I'm not writing a compiler, I'm limited to syntax as
allowed by Python. That means I have to give the record name
twice: once as the binding target, and once as an argument
to the "record" function.
- The trickiest bit is defining the record class' __init__
method, since I don't know how many arguments it will need.
Nevertheless, here you go. This was about 5 minutes work. Not too bad for a
first draft.
def record(name, fieldnames, verbose=False):
if isinstance(fieldnames, str):
fieldnames = fieldnames.split()
class Inner(object):
__slots__ = fieldnames
def __str__(self):
fields = ', '.join([repr(obj) for obj in self._fields])
return "record %s(%s)" % (type(self).__name__, fields)
__repr__ = __str__
@property
def _fields(self):
return [getattr(self, name) for name in fieldnames]
Inner.__name__ = name
ns = {}
template = "def __init__(self, %s):\n" % ', '.join(fieldnames)
for name in fieldnames:
template += ' self.%s = %s\n' % (name, name)
if verbose:
print("Using __init__ template:")
print(template)
exec(template, ns, ns)
Inner.__init__ = ns['__init__']
return Inner
py> date = record('date', 'year month day')
py> x = date(2016, 8, 14)
py> x
record date(2016, 8, 14)
py> x.year = 1999
py> x
record date(1999, 8, 14)
That's pretty good actually. Although I noticed you used the __slots__
feature that you were scathing about earlier on! (Also that you've
thrown in examples of using decorators, and using exec(), free of charge..)
There are a number of ways this approach differs from the native record
example (but I acknowledge your example was put together as a quick demo):
* The fields in my record are known at compile time; using the wrong
name will usually be picked immediately. As will using the wrong number
of initialisers.
* (My record fields have other features: aliases for any field; the
ability to access by index, doing len() etc; fast conversions to and
from a list; also the ability to create packed records that exactly
match C structs, and so on.)
* Each defined record in my scheme ('date' and so on) is a distinct
type. If I use the Python record() to create two records R and S, then
type(R) and type(S) seem to give the same result (both 'type').
* Record names and fields are case insensitive in my implementation.
* The Python version can't take advantage of a simple and fast native
implementation of records. (A simple loop creating a list of a few
million date records, excluding overheads, took 12 times as long Python
3, as my language. PyPy only took twice as long, but it's likely just
taking advantage of the big loop.
* But the biggest problem I think, as I hinted above, is that this is
not a standard part of the language. It's same thing with trying to fix
C issues by writing macros; everyone will roll their own versions. Or
not bother at all.
--
Bartc
--
https://mail.python.org/mailman/listinfo/python-list