PEP 318: Can't we all just get along?

K

Kevin Smith

For what it's worth, I wrote the original PEP 318. I probably wasn't
qualified, but I just wanted a nice simple way to declare class methods
without having to repeat the function name. After submitting it to BDFL
for approval, more work was needed and the discussion of PEP 318 on
python-dev increased rapidly. It was evident that I was in over my head,
so I asked more someone more experienced to take over.

I guess others had bigger plans for my proposal that I had planned. It
has turned into the "solution" to many problems: type checking (both
arguments and returned values), metaclasses, metadata, interfaces,
function attributes, etc.). Unfortunately, in the process, this simple
request for syntactical sugar has turned into a monstrosity. In my
opinion, none of the proposed syntaxes really seem Pythonic. This PEP
just seems to be trying to solve too many problems.

Bear with me, but I'd like to propose one more syntax that is simple,
easy for newbies to understand, and nowhere near as powerful as the
current PEP's syntax. However, it doesn't add incoherent, arbitrary
syntax either.

def classmethod foo(x, y, z):
pass

That's it. One "decorator" that is a callable object that takes a
method as it's only argument. No expressions, lists, tuples, etc. Just
one callable object. Ok, if you absolutely must have more than one.

def classmethod synchronized foo(x, y, z):
pass

Once again, no expressions. I know that this isn't going to solve
everyone's type-checking, metadata, and function attribute problems, but
let's face it, using this PEP for all of those things just creates ugly
syntax. There must be more Pythonic ways to do those things in their
own PEPs.
 
P

Paul Morrow

Kevin said:
For what it's worth, I wrote the original PEP 318. I probably wasn't
qualified, but I just wanted a nice simple way to declare class methods
without having to repeat the function name. After submitting it to BDFL
for approval, more work was needed and the discussion of PEP 318 on
python-dev increased rapidly. It was evident that I was in over my head,
so I asked more someone more experienced to take over.

I guess others had bigger plans for my proposal that I had planned. It
has turned into the "solution" to many problems: type checking (both
arguments and returned values), metaclasses, metadata, interfaces,
function attributes, etc.). Unfortunately, in the process, this simple
request for syntactical sugar has turned into a monstrosity. In my
opinion, none of the proposed syntaxes really seem Pythonic. This PEP
just seems to be trying to solve too many problems.

Bear with me, but I'd like to propose one more syntax that is simple,
easy for newbies to understand, and nowhere near as powerful as the
current PEP's syntax. However, it doesn't add incoherent, arbitrary
syntax either.

def classmethod foo(x, y, z):
pass

That's it. One "decorator" that is a callable object that takes a
method as it's only argument. No expressions, lists, tuples, etc. Just
one callable object. Ok, if you absolutely must have more than one.

def classmethod synchronized foo(x, y, z):
pass

Once again, no expressions. I know that this isn't going to solve
everyone's type-checking, metadata, and function attribute problems, but
let's face it, using this PEP for all of those things just creates ugly
syntax. There must be more Pythonic ways to do those things in their
own PEPs.

Nope. That's using static declarations. We're a dynamically typed
language as much as possible. Isn't there something that doesn't
require any additional grammar words to identify classmethods and
staticmethods? Isn't there something, inherent in the way static,
class, and instance methods 'look' naturally, that would tell us what
they are?
 
P

Peter Hansen

Paul said:
Nope. That's using static declarations. We're a dynamically typed
language as much as possible. Isn't there something that doesn't
require any additional grammar words to identify classmethods and
staticmethods?

It looks to me as though Kevin is not suggesting keywords, but
callables. In other words he would consider this valid, provided
my_own_decorator was a callable.

def my_own_decorator foo(x, y, z):
pass


-Peter
 
M

Michael J. Fromberger

Bear with me, but I'd like to propose one more syntax that is simple,
easy for newbies to understand, and nowhere near as powerful as the
current PEP's syntax. However, it doesn't add incoherent, arbitrary
syntax either.

def classmethod foo(x, y, z):
pass

That's it. One "decorator" that is a callable object that takes a
method as it's only argument. No expressions, lists, tuples, etc. Just
one callable object. Ok, if you absolutely must have more than one.

def classmethod synchronized foo(x, y, z):
pass

Once again, no expressions. I know that this isn't going to solve
everyone's type-checking, metadata, and function attribute problems, but
let's face it, using this PEP for all of those things just creates ugly
syntax. There must be more Pythonic ways to do those things in their
own PEPs.

Kevin,

+1 for this idea.

I completely agree that the original PEP-318 proposed a simple and
reasonable idea, and I too have been much dismayed by all that seems to
have been grafted onto it since. This latest idea of yours certainly
sounds a lot more reasonable than anything else I've seen so far. It
solves the original well-defined problem cleanly, and probably solves
some of the other proposed uses too.

I would even go so far as to say: Go ahead and restrict it to at most
one callable object. If someone really wants to define a flaming hot
crossed synchronized blue classmethod foo, they can bloody well define a
callable object that has the (in)appropriate behaviour.

I hope that perhaps cooler heads (such as yours) will prevail, and keep
Python's syntax and semantics relatively neat and tidy. We don't need
the One PEP to Rule Them All, in the Land of Python where the shadows --
so far -- do not lie.

-M, "one more PEP out of you, mister, and you're history!" ;)
 
P

Paul Morrow

Peter said:
It looks to me as though Kevin is not suggesting keywords, but
callables. In other words he would consider this valid, provided
my_own_decorator was a callable.

def my_own_decorator foo(x, y, z):
pass


-Peter

Oh, sorry, I wasn't reading that closely enough. Hmmmm... it's an
interesting idea. It's not ugly...

First though, I still believe that we should exploit existing
conventions (recommended coding practices) as a way of getting 'free'
declarations for class, static, and instance methods (e.g. methods whose
first param is 'self' are instance methods, etc.). That feels very
pythonic to me, just as we use naming conventions to distinguish public,
private, and semi-private methods.

But I think that, where we want to provide additional info about a
method, and there are no conventions to take advantage of, Kevin's
suggestion does have some appeal.

Questions (for Kevin):

1. Would

def deco1 foo(a, b, c): pass

be the same as (just syntactic sugar for)

def foo(a, b, c): pass
foo = deco1(foo)

or would it mean something else?

2. Would

def deco1 deco2 foo(a, b, c): pass

be the same as

def foo(a, b, c): pass
foo = deco1(deco2(foo))

or

def foo(a, b, c): pass
foo = deco2(deco1(foo))


3. Would there be any restrictions on what a decorator could *do* to the
method it was passed? e.g. Could it change:

* the method's name (which could of course affect the method's
visibility: public|private|semi-private)?
* whether the method was a static, class, or instance method?
* the method's signature (formal parameter names, parameter order,
defaults values)?
 
D

Doug Holton

Michael said:
Kevin,

+1 for this idea.

I posted the same thing to python-dev (although it took a week for the
post to appear) and some guy said -infinity or something smart like that.
I think it is simpler to use keywords for classmethod and staticmethod
at least. Those are builtin features of python, just like they are
builtins for java (which has keywords for its builtins like static,
abstract, interface, etc.).
I propose this *in addition* to whatever decorator syntax we add. We
shouldn't consider staticmethod and classmethod as decorators.
 
B

Bruno Desthuilliers

Kevin said:
For what it's worth, I wrote the original PEP 318. I probably wasn't
qualified, but I just wanted a nice simple way to declare class methods
without having to repeat the function name. After submitting it to BDFL
for approval, more work was needed and the discussion of PEP 318 on
python-dev increased rapidly. It was evident that I was in over my head,
so I asked more someone more experienced to take over.

I guess others had bigger plans for my proposal that I had planned. It
has turned into the "solution" to many problems: type checking (both
arguments and returned values), metaclasses, metadata, interfaces,
function attributes, etc.). Unfortunately, in the process, this simple
request for syntactical sugar has turned into a monstrosity. In my
opinion, none of the proposed syntaxes really seem Pythonic. This PEP
just seems to be trying to solve too many problems.

Bear with me, but I'd like to propose one more syntax that is simple,
easy for newbies to understand, and nowhere near as powerful as the
current PEP's syntax. However, it doesn't add incoherent, arbitrary
syntax either.

def classmethod foo(x, y, z):
pass

That's it. One "decorator" that is a callable object that takes a
method as it's only argument. No expressions, lists, tuples, etc. Just
one callable object.

+2 for me.

Ok, if you absolutely must have more than one.
> def classmethod synchronized foo(x, y, z):
> pass

No. If you want more than one, provide your own decorator, ie :

def synchronizedClassmethod(method):
return synchronized(classmethod(method))

def synchronizedClassmethod foo(x, y, z):
pass

My 2 eurocents
Bruno
 
D

Dan Sommers

First though, I still believe that we should exploit existing
conventions (recommended coding practices) as a way of getting 'free'
declarations for class, static, and instance methods (e.g. methods
whose first param is 'self' are instance methods, etc.). That feels
very pythonic to me, just as we use naming conventions to distinguish
public, private, and semi-private methods.

I don't understand how assigning semantic significance to a "recommended
coding practice" is Pythonic.

In the face of ambiguity, refuse to guess.

Explicit is better than implicit.

I think that the idea that started this thread is just right:

def decorator function( arguments ): pass

is exactly equivalent to

def function( arguments ): pass
decorator( function )

Note that:

- the decorator is right next to the function name
- the decorator is outside the code of the function
- there are no new keywords or punctuation
- it's dynamic, flexible, and extensible

It's also abusable, but that's the nature of a language as dynamic as
Python.

Regards,
Dan
 
P

Paul Morrow

Dan said:
I don't understand how assigning semantic significance to a "recommended
coding practice" is Pythonic.

In the face of ambiguity, refuse to guess.

Explicit is better than implicit.

Good conventions prevent ambiguity.

class Foo:
def method1(self, a, b): pass # clearly an instance method
def method2(cls, a, b): pass # clearly a class method
def method3(a, b): pass # clearly a static method


When declarations conflict with conventions, it makes us wonder what the
author really intended.

def staticmethod setX(self, x):
self.x = x
>
> I think that the idea that started this thread is just right:
>

You're right, this thread is about decorators, not about more
opportunities for dynamic typing. I just wanted to remind us that
decorators are a lot like static typing, which isn't terribly pythonic.
 
K

Kevin Smith

In said:
1. Would

def deco1 foo(a, b, c): pass

be the same as (just syntactic sugar for)

def foo(a, b, c): pass
foo = deco1(foo)

or would it mean something else?

Sorry, I guess I should have put the equivalent current Python code in
my first post. The example above is exactly what I meant.

2. Would

def deco1 deco2 foo(a, b, c): pass

be the same as

def foo(a, b, c): pass
foo = deco1(deco2(foo))

or

def foo(a, b, c): pass
foo = deco2(deco1(foo))

I would choose the first option since it's easier to transform the
string "deco1 deco2 foo" to "deco1(deco2(foo))" while I'm reading it (i.
e. it fits my head :) ).

3. Would there be any restrictions on what a decorator could *do* to
the method it was passed? e.g. Could it change:

* the method's name (which could of course affect the method's
visibility: public|private|semi-private)?

Nope, beginners wouldn't understand it immediately. That's just too
magical.
* whether the method was a static, class, or instance method?

Yes.
* the method's signature (formal parameter names, parameter order,
defaults values)?

I guess, in theory, it could since the object returned by the
"decorator" (I really hate that term) could return a completely
different object, but I wouldn't suggest it.
 
M

Michael J. Fromberger

Paul Morrow said:
Good conventions prevent ambiguity.

class Foo:
def method1(self, a, b): pass # clearly an instance method
def method2(cls, a, b): pass # clearly a class method
def method3(a, b): pass # clearly a static method

I think this is a terrible idea. What you are proposing is essentially
that Python should assign special meaning to the identifiers "self" and
"cls" when they appear as the first variable in a method definition
inside a class. I am having a hard time thinking of anything LESS in
the spirit of Python, short of writing in unstructured BASIC.

Good conventions do not prevent ambiguity, they merely help alleviate
its effects. And a "convention" that is enforced by the translator is
no longer a convention, but a rule of the language.
When declarations conflict with conventions, it makes us wonder what the
author really intended.

def staticmethod setX(self, x):
self.x = x

I find this at least as easy to understand as the current idiom:

def setX(self, x):
self.x = x
setX = staticmethod(setX)
You're right, this thread is about decorators, not about more
opportunities for dynamic typing.

Actually, as I read it, this thread is about getting back to the
original question PEP-318 was intended to answer, rather than all the
other cruft that the "decorators" idea has become laden with.

-M
 
A

Aahz

Bear with me, but I'd like to propose one more syntax that is simple,
easy for newbies to understand, and nowhere near as powerful as the
current PEP's syntax. However, it doesn't add incoherent, arbitrary
syntax either.

def classmethod foo(x, y, z):
pass

You can't do ``grep "def foo"``; therefore, Guido rejected this. (It's
a tiny bit more complicated than that, but that's the essence.)
--
Aahz ([email protected]) <*> http://www.pythoncraft.com/

"To me vi is Zen. To use vi is to practice zen. Every command is a
koan. Profound to the user, unintelligible to the uninitiated. You
discover truth everytime you use it." (e-mail address removed)
 
D

Dan Sommers

Dan Sommers wrote:
Good conventions prevent ambiguity.
class Foo:
def method1(self, a, b): pass # clearly an instance method
def method2(cls, a, b): pass # clearly a class method
def method3(a, b): pass # clearly a static method

class Bar:
def method4( _, b ): pass # clearly a method
def method5( klass, b ): pass # clearly something else
def method6( uovo, b ): pass # clearly an Italian program(mer)
When declarations conflict with conventions, it makes us wonder what
the author really intended.

I agree completely. I have often noted that when a comment doesn't
match the code, they are *both* wrong.

If we wonder what the author intended, what should Python do?
You're right, this thread is about decorators, not about more
opportunities for dynamic typing. I just wanted to remind us that
decorators are a lot like static typing, which isn't terribly
pythonic.

I readily admit that my background is static typing: BASIC, FORTRAN,
assembly, C, etc.; my (arguably naïve) lisp and Python programs are
devoid of most of the dynamic typing I see here on c.l.p. OTOH, does
anyone want/need "dynamic decorators"? Is there a real use case for
code like this?

class C:
def m( self ): pass
m = decorate( m )

decorate = some_function
c1 = C( )

decorate = another_function
c2 = C( )

Regards,
Dan
 
D

djw

Bruno said:
+2 for me.




No. If you want more than one, provide your own decorator, ie :

def synchronizedClassmethod(method):
return synchronized(classmethod(method))

def synchronizedClassmethod foo(x, y, z):
pass

My 2 eurocents
Bruno

+10
 
V

Ville Vainio

Kevin> Bear with me, but I'd like to propose one more syntax that
Kevin> is simple, easy for newbies to understand, and nowhere near
Kevin> as powerful as the current PEP's syntax. However, it
Kevin> doesn't add incoherent, arbitrary syntax either.

Kevin> def classmethod foo(x, y, z):
Kevin> pass


Kevin> Once again, no expressions. I know that this isn't going
Kevin> to solve everyone's type-checking, metadata, and function
Kevin> attribute problems, but let's face it, using this PEP for
Kevin> all of those things just creates ugly syntax. There must
Kevin> be more Pythonic ways to do those things in their own PEPs.

Python is not anymore in the blissful stage of being able to
incorporate zillions of features that could be provided by a single
syntactic addition, and relatively comfortably as well. Long
decorators w/ arguments is where the decorator syntax is going to pay
off.

I don't even use static/classmethods, but I can imagine several
frameworks can use parametrized decorators to their advantage.
 
P

Paul Morrow

Michael said:
I think this is a terrible idea. What you are proposing is essentially
that Python should assign special meaning to the identifiers "self" and
"cls" when they appear as the first variable in a method definition
inside a class. I am having a hard time thinking of anything LESS in
the spirit of Python, short of writing in unstructured BASIC.

'self' already has a special meaning to experienced Python developers,
by virtue of its role in the recommended coding practices. And if you
ask experienced Python developers who understand the difference between
class, static, and instance methods, to guess what kind of method this is...

def foo(cls, a, b): pass

....I bet the majority would correctly guess "class". So these words
already do have special significance to *us* when used in the first
parameter position. Why not make them significant to the Python system too?

Good conventions do not prevent ambiguity, they merely help alleviate
its effects.

Maybe the problem is with the word 'ambiguity'. How about adherence to
good coding conventions can keep things from being interpreted in
multiple ways.

And a "convention" that is enforced by the translator is
no longer a convention, but a rule of the language.

Right. So a convention becomes a rule. Sounds like a natural evolution.


I find this at least as easy to understand as the current idiom:

def setX(self, x):
self.x = x
setX = staticmethod(setX)

Me too, but they both have problems. The staticmethod declaration is in
conflict with the use of 'self' as the first parameter. And they both
require more code than necessary. Just as it would be unecessary to say...

def private __foo(self): pass

Declarations aren't needed when an obvious interpretation of the code
conveys the desired meaning.
 
B

Bengt Richter

Good conventions prevent ambiguity.
Conventions don't direct computation though, they just direct human interpretation.
That is, unless you use convention-patterns as you propose for self etc., and then
they aren't just conventions. (As you may infer, my second thoughts on this are
not supportive of overloading naming. I thought it was not that "fragile" in the
narrow context of ordinary method definition, but I think it's a step on a slippery
slope to name-tricks that too many clever people may be tempted to step onto ;-)
class Foo:
def method1(self, a, b): pass # clearly an instance method
def method2(cls, a, b): pass # clearly a class method
def method3(a, b): pass # clearly a static method
def method4(*args): pass # clearly different ;-)
When declarations conflict with conventions, it makes us wonder what the
author really intended.
Sure, that's natural. Depending on the author, you might look for something
interesting ;-)
def staticmethod setX(self, x):
self.x = x


You're right, this thread is about decorators, not about more
opportunities for dynamic typing. I just wanted to remind us that
decorators are a lot like static typing, which isn't terribly pythonic.
Maybe simple-name callables between the def and the function name
would cover most use cases, and you could let people do what they want
to set up the names. A single name could also be bound to a composition of
several things in a lambda or whatever, if handy for repetitive concise use.
import mydecorators
deco1 = mydecorators.deco1
deco2 = mydecorators.deco2factory('this', 'that', 'tother')
class Foo(object):
def staticmethod deco1 method1(x,y): pass
def deco2 method2(self): pass

A plain name series between def and arg list shouldn't be hard grammar-wise, IWT.
The idea is growing on me. It doesn't abuse naming itself.

Argh, must go ...

Regards,
Bengt Richter
 
M

Michael J. Fromberger

[email protected] (Aahz) said:
You can't do ``grep "def foo"``; therefore, Guido rejected this. (It's
a tiny bit more complicated than that, but that's the essence.)

I suppose that's understandable; but would it really be so much worse to
have to write:

egrep "def [_a-z0-9]* *foo"

or, even

grep "def" | grep "foo("

I respectfully submit that this, while it makes a certain amount of
sense, is probably not a very good design criterion for new syntactic
features.

-M
 
M

Michael J. Fromberger

Paul Morrow said:
'self' already has a special meaning to experienced Python developers,
by virtue of its role in the recommended coding practices.

That's true: And note, that its special meaning is interpreted not by
the Python translator, but by the Python programmer. It is my feeling
that it should remain thus.
And if you ask experienced Python developers who understand the
difference between class, static, and instance methods, to guess what
kind of method this is...

def foo(cls, a, b): pass

...I bet the majority would correctly guess "class".

Well, given just that definition, the guess would be incorrect. The
current release of Python would also require:

def foo(cls, a, b): pass # As defined above
foo = classmethod(foo)

Otherwise, it would be an instance method, not a class method. I hope
this illustrates why visual conventions are not always as obvious as
they seem, and should not be cast into the concrete of implementation.
Right. So a convention becomes a rule. Sounds like a natural
evolution.

And just like in natural evoluation, not all mutations are desirable. ;)
Declarations aren't needed when an obvious interpretation of the code
conveys the desired meaning.

What about when the obvious interpretation of the code does NOT convey
the desired meaning?

-M
 
P

Paul Morrow

Michael said:
Well, given just that definition, the guess would be incorrect. The
current release of Python would also require:

def foo(cls, a, b): pass # As defined above
foo = classmethod(foo)

Otherwise, it would be an instance method, not a class method. I hope
this illustrates why visual conventions are not always as obvious as
they seem, and should not be cast into the concrete of implementation.

Actually, it illustrates the importance of proof-reading a post before
posting it, which I didn't (sorry). I should've also stated that the
Python developers would be told that this is a special version of Python
that automatically determines the type (static|class|instance) of a
method. In that light, do you see how effective the visual convention
can be?
What about when the obvious interpretation of the code does NOT convey
the desired meaning?

Then we have a bad convention. One that I would absolutely agree should
not be enforced by the system. But that's not the case here. I believe
that the obvious interpretation of code written with this convention
would consistently and reliably convey the authors intent:

* If method's first param is 'self', it's an instance method.
* If method's first param is 'cls' or 'klass', it's a class method.
* All other methods are static methods.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,743
Messages
2,569,478
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top