[Python-3000] [Python-ideas] PEP 30xx: Access to Module/Class/Function Currently Being Defined (this)

Guido van Rossum guido at python.org
Thu May 3 03:29:53 CEST 2007


Summary for the impatient: -1; the PEP is insufficiently motivated and
poorly specified.

> PEP: 3130
> Title: Access to Current Module/Class/Function
> Version: $Revision: 55056 $
> Last-Modified: $Date: 2007-05-01 12:35:45 -0700 (Tue, 01 May 2007) $
> Author: Jim J. Jewett <jimjjewett at gmail.com>
> Status: Draft
> Type: Standards Track
> Content-Type: text/plain
> Created: 22-Apr-2007
> Python-Version: 3.0
> Post-History: 22-Apr-2007
>
>
> Abstract
>
>     It is common to need a reference to the current module, class,
>     or function, but there is currently no entirely correct way to
>     do this.  This PEP proposes adding the keywords __module__,
>     __class__, and __function__.
>
>
> Rationale for __module__
>
>     Many modules export various functions, classes, and other objects,
>     but will perform additional activities (such as running unit
>     tests) when run as a script.  The current idiom is to test whether
>     the module's name has been set to magic value.
>
>         if __name__ == "__main__": ...
>
>     More complicated introspection requires a module to (attempt to)
>     import itself.  If importing the expected name actually produces
>     a different module, there is no good workaround.
>
>         # __import__ lets you use a variable, but... it gets more
>         # complicated if the module is in a package.
>         __import__(__name__)
>
>         # So just go to sys modules... and hope that the module wasn't
>         # hidden/removed (perhaps for security), that __name__ wasn't
>         # changed, and definitely hope that no other module with the
>         # same name is now available.
>         class X(object):
>             pass
>
>         import sys
>         mod = sys.modules[__name__]
>         mod = sys.modules[X.__class__.__module__]

You're making this way too complicated.  sys.modules[__name__] always
works.

>     Proposal:  Add a __module__ keyword which refers to the module
>     currently being defined (executed).  (But see open issues.)
>
>         # XXX sys.main is still changing as draft progresses.  May
>         # really need sys.modules[sys.main]
>         if __module__ is sys.main:    # assumes PEP (3122), Cannon
>             ...

PEP 3122 is already rejected.

> Rationale for __class__
>
>     Class methods are passed the current instance; from this they can

"current instance" is confusing when talking about class method.
I'll assume you mean "class".

>     determine self.__class__ (or cls, for class methods).
>     Unfortunately, this reference is to the object's actual class,

Why unforunately?  All the semantics around self.__class__ and the cls
argument are geared towards the instance's class, not the lexically
current class.

>     which may be a subclass of the defining class.  The current
>     workaround is to repeat the name of the class, and assume that the
>     name will not be rebound.
>
>         class C(B):
>
>             def meth(self):
>                 super(C, self).meth() # Hope C is never rebound.
>
>         class D(C):
>
>             def meth(self):
>                 # ?!? issubclass(D,C), so it "works":
>                 super(C, self).meth()
>
>     Proposal: Add a __class__ keyword which refers to the class
>     currently being defined (executed).  (But see open issues.)
>
>         class C(B):
>             def meth(self):
>                 super(__class__, self).meth()
>
>     Note that super calls may be further simplified by the "New Super"
>     PEP (Spealman).  The __class__ (or __this_class__) attribute came
>     up in attempts to simplify the explanation and/or implementation
>     of that PEP, but was separated out as an independent decision.
>
>     Note that __class__ (or __this_class__) is not quite the same as
>     the __thisclass__ property on bound super objects.  The existing
>     super.__thisclass__ property refers to the class from which the
>     Method Resolution Order search begins.  In the above class D, it
>     would refer to (the current reference of name) C.

Do you have any other use cases?  Because Tim Delaney's 'super'
implementation doesn't need this.

I also note that the name __class__ is a bit confusing because it
means "the object's class" in other contexts.

> Rationale for __function__
>
>     Functions (including methods) often want access to themselves,
>     usually for a private storage location or true recursion.  While
>     there are several workarounds, all have their drawbacks.

Often?  Private storage can just as well be placed in the class or
module.  The recursion use case just doesn't occur as a problem in
reality (hasn't since we introduced properly nested namespaces in
2.1).

>         def counter(_total=[0]):
>             # _total shouldn't really appear in the
>             # signature at all; the list wrapping and
>             # [0] unwrapping obscure the code
>             _total[0] += 1
>             return _total[0]
>
>         @annotate(total=0)

It makes no sense to put dangling references like this in motivating
examples.  Without the definion of @annotate the example is
meaningless.

>         def counter():
>             # Assume name counter is never rebound:

Why do you care so much about this?  It's a vanishingly rare situation
in my experience.

>             counter.total += 1
>             return counter.total

You're abusing function attributes here IMO.  Function attributes are
*metadata* about the function; they should not be used as per-function
global storage.

>         # class exists only to provide storage:

If you don't need a class, use a module global.  That's what they're
for.  Name it with a leading underscore to flag the fact that it's an
implementation detail.

>         class _wrap(object):
>
>             __total = 0
>
>             def f(self):
>                 self.__total += 1
>                 return self.__total
>
>         # set module attribute to a bound method:
>         accum = _wrap().f
>
>         # This function calls "factorial", which should be itself --
>         # but the same programming styles that use heavy recursion
>         # often have a greater willingness to rebind function names.
>         def factorial(n):
>             return (n * factorial(n-1) if n else 1)
>
>     Proposal: Add a __function__ keyword which refers to the function
>     (or method) currently being defined (executed).  (But see open
>     issues.)
>
>         @annotate(total=0)
>         def counter():
>             # Always refers to this function obj:
>             __function__.total += 1
>             return __function__.total
>
>         def factorial(n):
>             return (n * __function__(n-1) if n else 1)
>
>
> Backwards Compatibility
>
>     While a user could be using these names already, double-underscore
>     names ( __anything__ ) are explicitly reserved to the interpreter.
>     It is therefore acceptable to introduce special meaning to these
>     names within a single feature release.
>
>
> Implementation
>
>     Ideally, these names would be keywords treated specially by the
>     bytecode compiler.

That is a completely insufficient attempt at describing the semantics.

>     Guido has suggested [1] using a cell variable filled in by the
>     metaclass.
>
>     Michele Simionato has provided a prototype using bytecode hacks
>     [2].  This does not require any new bytecode operators; it just
>     modifies the which specific sequence of existing operators gets
>     run.

Sorry, bytecode hacks don't count as a semantic specification.


> Open Issues
>
>     - Are __module__, __class__, and __function__ the right names?  In
>       particular, should the names include the word "this", either as
>       __this_module__, __this_class__, and __this_function__, (format
>       discussed on the python-3000 and python-ideas lists) or as
>       __thismodule__, __thisclass__, and __thisfunction__ (inspired
>       by, but conflicting with, current usage of super.__thisclass__).
>
>     - Are all three keywords needed, or should this enhancement be
>       limited to a subset of the objects?  Should methods be treated
>       separately from other functions?

What do __class__ and __function__ refer to inside a nested class or
function?

> References
>
>     [1] Fixing super anyone?  Guido van Rossum
>         http://mail.python.org/pipermail/python-3000/2007-April/006671.html
>
>     [2] Descriptor/Decorator challenge,  Michele Simionato
>         http://groups.google.com/group/comp.lang.python/browse_frm/thread/a6010c7494871bb1/62a2da68961caeb6?lnk=gst&q=simionato+challenge&rnum=1&hl=en#62a2da68961caeb6
>
>
> Copyright
>
>     This document has been placed in the public domain.
>
>
> 
> Local Variables:
> mode: indented-text
> indent-tabs-mode: nil
> sentence-end-double-space: t
> fill-column: 70
> coding: utf-8
> End:

---
--Guido van Rossum (home page: http://www.python.org/~guido/)


More information about the Python-3000 mailing list