[Python-Dev] Draft proposal: Implicit self in Python 3.0

Alexander Kozlovsky alexander.kozlovsky at gmail.com
Thu Jan 5 22:56:01 CET 2006


Hello!

I have some proposal for Python 3.0 (interesting one, from my point
of view). I'm sorry for my English, it is not very good.


Abstract
========

There are three different peculiarity in Python 2.x
in respect of 'self' method argument:

1. Each method must have explicit 'self' argument in its argument list
2. This argument must be used explicitly for instance's attribute value
   assignment
3. The count of explicit arguments in method definition differs from
   count of explicit parameters when method is called

Example 1 (Python 2.x):
-----------------------

    class Foo:
        def __init__(self, x):   # 1: Explicit 'self' argument
            self.x = x           # 2: 'self' must be used explicitly
        def bar(self, a, b):     # 3: There are three arguments...
            print self.x + a + b

    Foo(10).bar(20, 30)          # ...but only two explicit parameters
                                 #    is presented

This document proposes to change this, as the next example shows:

Example 2 (Python 3.0):
-----------------------

    class Foo:
        def __init__(x):         # 1: Implicit self
            .x = x               # 2: Brief form of: self.x = x
        def bar(a, b):           # 3: Two arguments...
            print .x + a + b

    Foo(10).bar(20, 30)          # ...and exactly two parameters

According  Python Zen, "Explicit is better then implicit", but
"practicality beats purity" and "Readability counts" ;-)

This draft document describes high-level semantic of proposed changes
and doesn't discuss details of C implementation.

    
Rationale
=========

When programmer tries to pick up some new programming language from
different possibilities (e.g. Smalltalk, Python, Perl, ...), he often
bases his choice on secondary and non-essential details. Almost any
language has peculiarities, which can distract potential users
from language evaluation. Examples of such warts may be #@$&% perlish
syntax in Ruby, abundance of parenthesis in Lisp and Schema, and
explicit 'self' argument in Python.

Of course, from technical point of view, such peculiarities are
completely non-issue. Parenthesis is not problem in Lisp if you use
a good text editor, perlisms in Ruby can be avoided, etc. But from
sociological and usability points of view, such warts can cause
remarkable hurt, because they affect first impression about language.

In many superficial language comparisons Python's OO approach dismissed
as after-thought because of the explicit 'self'. Example:

   But the most important aspect, why I am using Ruby instead of
   Python or Perl are the object-orientated features of Ruby, and
   that Ruby was designed object-oriented right from beginning,
   unlike Python and Perl where object-orientation was added on
   later. You can recognize this in e.g. in Python very good,
   because the first parameter (often named self) of every method of
   a class is the object on which the method is called

   (http://www.ntecs.de/old-hp/s-direktnet/rb/ruby.pdf)

   
Of course, this words about Python are not true. Python was
object-oriented from the beginning, and explicit 'self' is intentional
design decision, which is elegant in some aspects. But from
pragmatical point of view, implicit 'self' in Python 3.0 may
lower entry barrier for many people and assist Python's wide adoption.

The proposed change is not backward-compatible with Python 2.x

Detailed proposals
==================

1. 'self' becomes a keyword, and may be used inside function
   definition to denote a special implicit function argument

2. New syntax shortcut is introduced: '.attr' inside a function
   is exact equivalent to 'self.attr'. Full syntax can be used
   as well
   
3. 'class' keyword can be used in two different syntactical
   construction:
   a) If 'class' keyword immediately followed by identifier,
      then it is usual class definition.
   b) In all other cases (inside a function) 'class' keyword
      denotes value of implicit function argument

   >>> # in Python 3.0:
   >>> def sample(a, b):
   ...     class C: pass         # ordinary class definition
   ...     print class.__name__  # 'class' is implicit argument
   ...

4. Each function (not only class methods) accepts two implicit
   arguments, 'class' and 'self'. With ordinary function call,
   this arguments has 'None' value

   >>> def f(a, b):
   ...     return [class, self, a, b]
   ...
   >>> f(1, 2)
   [None, None, 1, 2]

   Implicit 'class' and 'self' attributes don't participates in
   partial function application

5. Each function have two constant attributes, __class__ and __self__,
   both of them have value 'None'

   >>> f.__class__, f.__self__
   (None, None)

6. New builtin function bind(func, self_, [class_]) is introduced.
   The result is a new function with the same name,
   __self__ and __class__ attributes of which are equal to
   self_ and class_, correspondly. If bind function is called
   without class_ argument, then __class__ value in new function
   remains the same as in previous ones.

   >>> f
   <function f[None, None] at 0x01636F30>
   >>> g = bind(f, 'foo', 'bar')
   >>> g
   <function f['foo', 'bar'] at 0x01640770>
   >>> g.__class__, g.__self__
   ('bar', 'foo')

   When function is called, its __class__ and __self__ attributes
   are accessible inside function body as 'class' and 'self':

   >>> g(10, 20)
   ['foo', 'bar', 10, 20]

   Once bounded function can be rebinded easily:

   >>> bind(g, 100, 200)
   <function f[100, 200] at 0x0165CAB0>
   
7. In process of function extraction from class or instance dictionary
   it became bound automatically (the machinery is in
   object.__getattribute__ and type.__getattribute__ methods):

   >>> def func(a, b):
   ...     return [class, self, a, b]
   ...
   >>> def Class:
   ...     func = func
   ...
   >>> instance = Class()
   >>> func(10, 20)
   [None, None, 10, 20]
   >>> Class.func(10, 20)
   [<class '__main__.Class'>, None, 10, 20]
   >>> instance.func(10, 20)
   [<class '__main__.Class'>, <__main__.Class object at 0x016434F0>, 10, 20]
   
   More examples with metaclasses:

   >>> def func(a, b):
   ...     return [class, self, a, b]
   ...
   >>> class Meta(type):
   ...     func = func
   ...
   >>> class Class:
   ...     __metaclass__ = Meta
   ...
   >>> instance = Class()
   >>> func(10, 20)
   [None, None, 10, 20]
   >>> Meta.func(10, 20)
   [<class '__main__.Meta'>, None, 10, 20]
   >>> Class.func(10, 20)
   [<class '__main__.Meta'>, <class '__main__.Class'>, 10, 20]
   >>> instance.func(10, 20)
   Traceback (most recent call last):
     ...
   AttributeError: 'Class' object has no attribute 'func'

9. Methods can be overloaded this way:

   >>> class MyList(list):
   ...     def __getitem__(key):
   ...         print "overloaded method!"
   ...         return super(MyList, self).__getitem__(key)
   ...     def __setitem__(key, value):
   ...         print "overloaded without super"
   ...         bind(list.__setitem__, self)(key, value)
   ...
   
10. It is interesting that with this semantics there are no need in
   @classmethod and @staticmethod decorators:
   - If you want to create class method, simply don't use 'self' argument
   - If you want to create static method, don't use 'class' and 'self'
   argument

   >>> class Example:
   ...     class_var = 100
   ...     def my_class_method():   # no need for @classmethod
   ...         class.class_var += 1
   ...     def my_static_method():  # no need for @staticmethod
   ...         print "hello!"
   ...     def usual_method():
   ...         print .name
   ...



What do you think about this?
   

Best regards,
 Alexander                          mailto:kozlovsky at mail.spbnit.ru



More information about the Python-Dev mailing list