Continuing the definition for a class feels to me like a strong anti-pattern for Python. If you absolutely must do this, I guess the presented decorator is available. It should be discouraged though, not be part of new syntax.

I believe that if you find yourself doing this you should detect a bad choice smell, and think about expressing your goal with inheritance, delegation, or encapsulation instead.


On Sep 13, 2016 9:17 AM, "Pim Schellart" <p.schellart@princeton.edu> wrote:
Dear Python Developers,

we have a potential idea for enhancing Python.
Below you will find what the PEP might look like.
A reference implementation has been written and will be posted in a follow up message.
We are look forward to hearing your feedback and ideas.

Kind regards,

Pim Schellart & Nate Lust

Abstract
========

This PEP proposes the introduction of new syntax to create a community standard,
readable way to continue a definition for classes which are already defined.


Rationale
=========

Extending existing classes with new members currently requires the members to be
defined outside of class scope and then assigned to the class, as in::

  def foo(self):
      pass
  A.foo = foo
  del foo

This pattern frequently occurs when extending classes from extension modules
written in other languages, but is also not uncommon in pure Python code.

This syntax is however cumbersome and error prone for the following reasons:

  1. The name of the new member might clash with an existing name at module
     scope.
  2. The developer might forget the assignment.
  3. The developer might forget to delete the temporary, leaving it at module
     scope where it is meaningless (or worse non functional as a stand alone
     function).

Alternatives are to use inheritance, lambda or a decorator.

Inheritance is not a good option for use with Python extension modules (written
in for instance C / C++).  The reason is that if in the other language the
inheritance relation is A<-B, and this is exposed to Python, we can't add
functionality to A in Python by subclassing A<-A' without also introducing B'
that inherits from B and A'.  Thus one change propagates all the way down the
inheritance chain.  Inheritance with pure Python classes exhibit the same issue.

It might be tempting to avoid (1) and (2) by subclassing, reusing the name of
the base class. However this approach suffers from two issues.  The first issue,
like above, has to do with the inheritance relation.  If A subclasses A and B
subclasses A (A<-A<-B), then calls to super in B may have unexpected behavior
(anything using the method resolution order may exhibit the same
unexpectedness).  The second issue arises when class B inherits from class A
before it is extended (A<-B) and then class A is extended though inheritance
(A<-A).  Class A will now have new methods and data members which are not
present in B, as it is a subclass only of the original A.  This would be
confusing to anyone examining B as it would be apparently missing members of
it's parent.

Adding attributes to a class using lambda is not equivalent because it only
allows for expressions and not statements.

A class decorator (say "@continue_class(A)" that takes methods and attributes
from the class and attaches them to the wrapped type) works, but is cumbersome
and requires a new class name to be introduced for each extended type.  Thus
suffering from the same problems numbered (1) and (2) above.

A function decorator could extend a class, but also requires defining a new name
and suffers from problems (1) and (2).

This proposal adds the keyword combination "continue class" which instructs the
interpreter to add members to an existing class.  This syntax requires no new
keywords and allows all existing Python code to continue to function with no
modification.

By combining existing keywords it avoids name clashes.  Precedents for this
exists in "is not" and "yield from".

Another big advantage of the "continue class" syntax is discoverability.  For
humans, the meaning of the syntax is evident, the class will be continued with
the following block.  Automated tools will also benefit from this syntax as
parsing a file to discover the complete definition of a class will be easier
than trying to detect 'monkey patching'.


Semantics
=========

The following two snippets are semantically identical::

  continue class A:
      x = 5
      def foo(self):
          pass
      def bar(self):
          pass

  def foo(self):
      pass
  def bar(self):
      pass
  A.x = 5
  A.foo = foo
  A.bar = bar
  del foo
  del bar


Alternatives
============

An alternative could be to allow a function definition to include a class
specifier as in::

  def A.foo(self):
    pass


Implementation
==============

Adapting Python's grammar to support the continue class keyword combination
requires modifying the grammar file to parse for the new keyword combination.
Additional changes to Python.asdl, ast.c, compile.c, symtable.c will be required
to process the parsed syntax to byte code.

A reference implementation (already written and posted separately) parses the
continue class line for the name of the class to be modified and loads it.  Next
the body of the block is compiled into a code object scoped by the name of the
class.  The class and the code are used as arguments to a new built-in module,
called __continue_class__.  This function evaluates the code block, passing the
results to setattr on the supplied class.
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/