What's the best way to minimize the need of run time checks?

BartC bc at freeuk.com
Sat Aug 13 16:59:41 EDT 2016


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



More information about the Python-list mailing list