[Python-Dev] Re: Extended Function syntax

Manuel Garcia mgarcia@cole-switches.com
Fri, 24 Jan 2003 12:26:48 -0800


I always considered

	stat_m = staticmethod(stat_m)
	class_m = classmethod(class_m)

to be less about an awkward notation, and more about the standard Python
idiom for one technique to keep namespaces clean of unused identifiers.

Like this:
	i = int(i) # i held a string, now holds an int
Instead of:
	i = int(i_string) # will never use i_string again

With the benefit of clean namespaces being less identifiers to trip over
when using any tool for introspection, preventing accidental references to
identifiers we really wanted to be temporary, etc.

In my mind, staticmethod(), classmethod(), and property() are the same, they
take functions and "package" them in a way suitable for implementing methods
or properties.  Since everyone loves functional programming :-/ , functions
that act on functions are nothing to be ashamed of, and don't deserve to be
hidden behind syntactical sugar.

(Things like

    stat_m = staticmethod(stat_m)

is one reason of why C++ and Java people consider Python "not really object
oriented", as if throwing keywords and keyword phrases at every damn OO
construct is the best way to be object oriented.  Yes, syntactical sugar
like

    def name() [staticmethod]:

helps Python gain more respect with the C++/Java crowd.)

The most explicit way of using staticmethod(), classmethod(), property()
forces you to take extra steps to keep your namespace clean:

    class klass01(object):

        def _pure_set_x(x): klass01.x = x
        static_set_x = staticmethod(_pure_set_x)

        def _pure_get_x(): return klass01.x
        static_get_x = staticmethod(_pure_get_x)

        def _pure_set_y(cls, y): cls.y = y
        class_set_y = classmethod(_pure_set_y)

        def _pure_get_y(cls): return cls.y
        class_get_y = classmethod(_pure_get_y)

        del _pure_set_x, _pure_get_x, _pure_set_y, _pure_get_y

        def _get_j(self): return self._j
        def _set_j(self, j): self._j = j
        j = property(_get_j, _set_j, None, 'dynamite!')

        del _get_j, _set_j

(<sheepishly> This argument is more about property() than staticmethod(),
classmethod().  I have to admit "stat_m = staticmethod(stat_m)" doesn't
really bother me very much.  I have a little more to say about staticmethod
below.)

I always wanted some syntactical sugar for this idiom:

    def _junk():
        a = long_expression01(x0, y0)
        b = long_expression02(x1, y1)
        c = long_expression03(x2, y2)
        return min(a,b,c), max(a,b,c)
    d, e = _junk()
    del _junk()

with the purpose being keeping a,b,c out of our nice clean namespace.

The notation would be:

    d, e = block:
        a = long_expression01(x0, y0)
        b = long_expression02(x1, y1)
        c = long_expression03(x2, y2)
        return min(a,b,c), max(a,b,c)

We would set up a property like this (if there is no benefit to having
_get_j and _set_j in the namespace):

    j = block:
        def _get_j(self): return self._j
        def _set_j(self, j): self._j = j
        return property(_get_j, _set_j, None, 'dynamite!')

I would like this way to define properties.  Everything is for "j" is kept
together, with the temporary names that make everything clear disappearing
when we leave the block.

Considering the most general form of the proposed extended function syntax:

    def name(arg, ...) [expr1, expr2, expr3]:
        ...body...

we would have instead:

    name = block:
        def _(arg, ...):
            ...body...
        return expr3(expr2(expr1(_)))

It takes 2 more lines, but you have the benefit of not having to explain
that

    [expr1, expr2, expr3]

means

    expr3(expr2(expr1(name)))

Also less likely to need a lambda, etc.

I admit this is almost no help at all for staticmethod(), classmethod()
except perhaps in this case:

    stat1, stat2, stat3 = block:
        def pure1():
            ...body...
        def pure2():
            ...body...
        def pure3():
            ...body...
        return staticmethod( (pure1, pure2, pure3) )

where staticmethod() now also accepts a tuple of functions.  Contrived, but
if you had more than one staticmethod, they would probably be grouped
together anyway.

We only saved 1 lousy line for 3 staticmethods.  However, we have made the
idea of turning pure functions into staticmethods explicit.  I admit we have
gained very little.

We gain nothing here (except dubious explicitness):

    stat1 = block:
        def pure1():
            ...body...
        return staticmethod(pure1)

instead of:

    def stat1():
        ...body...
    stat1 = staticmethod(stat1)

or:

    def stat1() [staticmethod]:
        ...body...

Some mention was made of classes for a similar extended syntax:

    klass1 = block:
        class klass1():
            ...body...
        return I02(I01(klass1))

Obvious problems:

All this stinks of trying to come up with a poor substitute of
"super-lambda" (a way to make an anonymous function allowing multiple lines
and statements).  I don't deny it.

The main problem is that on the right hand side of the equals sign is
something that is not an expression.  We could solve that problem with this
abomination:

    block:
        global d, e
        a = long_expression01(x0, y0)
        b = long_expression02(x1, y1)
        c = long_expression03(x2, y2)
        d,e = min(a,b,c), max(a,b,c)

I associate use of 'global' in my programs with a design flaw, I would not
defend this.

Finishing up:

    block:
        ...body...

when you don't care about the return value.

    g = block:
        ...body...
        yield x

Would be the same as:

    def g():
        ...body...
        yield x

but probably should raise an error because it doesn't match what happens
with 'return', not even matching in spirit.