[Python-ideas] pep-0484 - Forward references and Didactics - be orthogonal

Terry Reedy tjreedy at udel.edu
Tue Aug 25 17:15:08 CEST 2015


On 8/24/2015 12:19 AM, Prof. Dr. L. Humbert wrote:

> What students should be able to code:
>
> 1. varinat
> #-------------wishful----------------------------------\
> class Tree:
>      def __init__(self, left: Tree, right: Tree):
>          self.left = left
>          self.right = right

As you should know, at least after reading previous responses, making 
this work would require one of two major changes to Python class statements.

1. The class name has special (context sensitive) meaning in enclosed 
def statements.  The compiler would have to compile def statements 
differently than it would the same def statements not in a Tree class. 
It would then have to patch all methods after the class is created.  See 
the annoclass function below.

A proposal to make the definition name of a function special within its 
definition has already been rejected.

2. Class statements would initially create an empty class bound to the 
class name.  This could break back compatibility, and would require 
cleanup in case of a syntax error in the body. This would be similar to 
import statements initially putting a empty module in sys.modules to 
support circular imports.  This is messy and still bug prone is use.

> what students have to write instead:
>
> #-------------bad workaround----------------------------\
> class Tree:
>      def __init__(self, left: 'Tree', right: 'Tree'):
>          self.left = left
>          self.right = right

You did not say why you think this is bad.  Is it a) students have to 
type "'"s?, or b) the resulting annotations are strings instead of the 
class?  The latter can easily be fixed.

---
from types import FunctionType

def annofix(klass):
     classname = klass.__name__
     for ob in klass.__dict__.values():
         if type(ob) is FunctionType:
             annotations = ob.__annotations__
             for arg, anno in annotations.items():
                 if anno == classname:
                     annotations[arg] = klass
     return klass

@annofix
class Tree:
     def __init__(self, left: 'Tree', right: 'Tree'):
         self.left = left
         self.right = right

print(Tree.__init__.__annotations__)
# {'left': <class '__main__.Tree'>, 'right': <class '__main__.Tree'>}
---

An alternative is to use a placeholder object instead of the class name. 
This is less direct, not repeating the name of the class throughout the 
definition makes it easier to rename the class or copy methods to 
another class.

---
class Klass: pass
# An annotation object meaning 'the class this method is defined in'

def annofix2(klass):
     for ob in klass.__dict__.values():
         if type(ob) is FunctionType:
             annotations = ob.__annotations__
             for arg, anno in annotations.items():
                 if anno == Klass:
                     annotations[arg] = klass
     return klass

@annofix2
class Tree2:
     def __init__(self, left: Klass, right: Klass):
         self.left = left
         self.right = right

print(Tree2.__init__.__annotations__)
{'right': <class '__main__.Tree2'>, 'left': <class '__main__.Tree2'>}

-- 
Terry Jan Reedy



More information about the Python-ideas mailing list