Self function

S

Steve Howell

I propose a small piece of sugar. When a function is entered, Python
creates an ordinary local name in the function's local namespace, and
binds the function itself to that name. Two possibilities for the name
are `this` or `__this__`, analogous to `self` in methods and `__name__`
in modules.

If there is any support for this, I propose to send the following (long)
post to python-ideas. Feedback, corrections and suggestions welcome.

I totally support this proposal. I've definitely wasted time in the
past trying to invent my own workarounds for the use cases you
describe.

Obviously, there will be some bikeshed debates on __this__ vs.
__name__ vs. __func__, etc. I don't have an opinion there, just want
*something* handy for introspection, DRYness, etc.

A more interesting question is whether __this__ would just always be
there (that's my vote), or if you should have to apply a special
decorator to get it (which I oppose, but I can see some merits).
 
A

Aaron Brady

I totally support this proposal.   I've definitely wasted time in the
past trying to invent my own workarounds for the use cases you
describe.

Obviously, there will be some bikeshed debates on __this__ vs.
__name__ vs. __func__, etc.  I don't have an opinion there, just want
*something* handy for introspection, DRYness, etc.

A more interesting question is whether __this__ would just always be
there (that's my vote), or if you should have to apply a special
decorator to get it (which I oppose, but I can see some merits).

Here is how to get the function into the function as an argument, and
still permit recursive calls onto it.
.... def _inner( *ar, **kw ):
.... return f( g, *ar, **kw )
.... g= _inner
.... return g
........ def f( self, n ):
.... print( self, n )
.... return 1 if n== 1 else n* self( n- 1 )
....<function _inner at 0x00BA0D68> 7
<function _inner at 0x00BA0D68> 6
<function _inner at 0x00BA0D68> 5
<function _inner at 0x00BA0D68> 4
<function _inner at 0x00BA0D68> 3
<function _inner at 0x00BA0D68> 2
<function _inner at 0x00BA0D68> 1
5040

The function that is passed into the function is the decorated
function, not the original. The decorator function is also returned
from the decorator calls, so both the external caller and the internal
caller are calling the same function, '_inner' in the sample, which
then back-calls the original.

Please stay tuned for the post-graduate lecture. There will be time
for questions at the cookies-and-creme seminar afterward.
 
B

bearophileHUGS

wolfram.hinde...:
It is easy to change all references of the function name, except for
those in the function body itself? That needs some explantation.

I can answer this. If I have a recursive function, I may want to
create a similar function, so I copy and paste it, to later modify the
copied version. Then to change its name I usually don't use a search &
rename of its name into its block of code because it's usually
useless. In non-recursive functions the name of the function is stated
only once, at the top.

Recursive functions violate the DRY principle, that's the root of the
problem.

The auto(f) decorator shown later by Aaron Brady seems the best
solution found so far, it's so nice that it even seems practically
usable.

Bye,
bearophile
 
B

bearophileHUGS

Aaron Brady:
...     def _inner( *ar, **kw ):
...             return f( g, *ar, **kw )
...     g= _inner
...     return g

Looks nice, I'll try to the following variant to see if it's usable:

def thisfunc(fun):
"""Decorator to inject a default name of a
function inside a function"""
def _inner(*args, **kwds):
return fun(g, *args, **kwds)
g = _inner
g.__name__ = fun.__name__
g.__dict__.update(fun.__dict__)
g.__doc__ = fun.__doc__
g.__module__ = fun.__module__
return g

@thisfunc
def SOMEVERYUGLYNAME(this, n):
return 1 if n <= 1 else n * this(n - 1)

assert SOMEVERYUGLYNAME(6) == 2*3*4*5*6

Bye,
bearophile
 
A

Aaron Brady

Aaron Brady:



Looks nice, I'll try to the following variant to see if it's usable:

def thisfunc(fun):
    """Decorator to inject a default name of a
    function inside a function"""
    def _inner(*args, **kwds):
        return fun(g, *args, **kwds)
    g = _inner
    g.__name__ = fun.__name__
    g.__dict__.update(fun.__dict__)
    g.__doc__ = fun.__doc__
    g.__module__ = fun.__module__
    return g

@thisfunc
def SOMEVERYUGLYNAME(this, n):
    return 1 if n <= 1 else n * this(n - 1)

assert SOMEVERYUGLYNAME(6) == 2*3*4*5*6

Bye,
bearophile

/Actually/, the 'g' variable was spurious. FWIW.

def auto( f ):
def _inner( *ar, **kw ):
return f( _inner, *ar, **kw )
return _inner
 
M

MRAB

Steve said:
I totally support this proposal. I've definitely wasted time in the
past trying to invent my own workarounds for the use cases you
describe.

Obviously, there will be some bikeshed debates on __this__ vs.
__name__ vs. __func__, etc. I don't have an opinion there, just want
*something* handy for introspection, DRYness, etc.

A more interesting question is whether __this__ would just always be
there (that's my vote), or if you should have to apply a special
decorator to get it (which I oppose, but I can see some merits).
I'd say that __this__ is a little unclear, so I'd choose __func__.
 
S

Steven D'Aprano

One issue with automatically binding a local variable to the current
function is with nested functions:

def foo()
def bar():
# How do I call foo() from here?

As foo(), just like you would call foo() from outside of foo(). __this__
would refer to bar() inside bar().

My proposal isn't meant to solve the general case "how do I call any
function anywhere without knowing its name?". I don't think there is a
general solution to that problem (although obviously you can do it in
special cases). My proposal is sugar for a fairly general problem "how
should a function refer to itself?", not an alternative mechanism for
referring to functions outside of the usual namespace lookup mechanism.


One solution would be

def foo()
def bar(foo=__this__):
foo()


That would be an option, if the programmer had some reason for wanting to
do this.
 
R

Rhodri James

wolfram.hinde...:

I can answer this. If I have a recursive function, I may want to
create a similar function, so I copy and paste it, to later modify the
copied version. Then to change its name I usually don't use a search &
rename of its name into its block of code because it's usually
useless. In non-recursive functions the name of the function is stated
only once, at the top.

I'm sorry, but while I'm mildly positive towards the proposal (and more
so towards Aaron's decorator), I don't buy this argument at all. What
is broken about your editor's global search-and-replace function that
makes it "usually useless" for making these name changes?
 
A

Arnaud Delobelle

def auto( f ):
    def _inner( *ar, **kw ):
        return f( _inner, *ar, **kw )
    return _inner

Quoting myself near the start of this thread:

Here's an idea:
.... def boundf(*args, **kwargs):
.... return f(boundf, *args, **kwargs)
.... return boundf
 
L

Lie Ryan

Luis said:
Btw, is there any way to inject a name into a function's namespace? Following
the python tradition, maybe we should try to create a more general solution!

How about a mechanism to pass arguments that are optional to accept?

So (in the general case) the caller can call a function with certain
arguments, but unless the function explicitly accept it, the argument
will be discarded.

With this, the injecting a local would simply be a case of adding an "I
accept the argument" statement.


Sort of like this:

def func():
''' I reject '''
pass

func(@somearg='Feel free to accept this arg')

def func(@somearg):
''' I accept '''
print(somearg)

func(@somearg='Feel free to accept this arg')


Then on the case of injecting locals is simply by wrapping the function
with a function that will inject various argument to the function and
the function selectively choose which arguments they want to accept.

def needinfo(f):
# let's assume there is an inspect code here
func(@__name__=yournameis,
@__call__=yourfunction,
@__code__=yourcode,
@__yoursub__=youryo,
@__yourlim__=yoururso,
@__yourina__=yournisk,
@__yourlmes__=youridna,
@__yoursage__=yourpped
)

@needinfo
def func(@__name__, @__call__):
print 'Hi, my name is %s and to call me press %s' % (__name__,
__call__)

Does it sounds like a feature smell?
 
A

Aaron Brady

Quoting myself near the start of this thread:

Here's an idea:


...     def boundf(*args, **kwargs):
...         return f(boundf, *args, **kwargs)
...     return boundf

Yeah, but that was two days ago.
 
A

Aaron Brady

How about a mechanism to pass arguments that are optional to accept?

So (in the general case) the caller can call a function with certain
arguments, but unless the function explicitly accept it, the argument
will be discarded.

With this, the injecting a local would simply be a case of adding an "I
accept the argument" statement.
snip

You could write a decorator to do that with the inspect module, or you
could just accept a keyword dict for extra as-desired args.
 
F

Francis Carr

Scheme is arguably the programming language with the most support for
recursion, including the most complete support for tail-call
optimization, constructs like "letrec" to define collections of
multiply-recursive functions (which get used very frequently -- by no
means is it an uncommon situation, as you suggest in your initial
post), and hardly any iterative loops. Yet -- scheme does not provide
out-of-the-box support for your proposed "let-a-function-implicitly-
refer-to-itself" idea. This suggests that the idea itself runs
counter to more important aspects of a programming language.

In the special case of a self-recursive function, you would like re-
naming "to just work". Out of morbid curiosity, I am compelled to
ask... why do you expect that re-naming anything --- a function, a
class, a module, a variable, anything --- in just one place but not
all the others should ever, under any circumstances, "just work" ?
 
B

bearophileHUGS

Francis Carr:

I don't know who are you talking to, but I can give you few answers
anyway.
collections of multiply-recursive functions (which get used very frequently -- by no means is it an uncommon situation, as you suggest in your initial post),<

They may be frequent in Scheme (because it's often used as an almost
pure language), but in Python code they are quite rare. I have used
two mutual recursive functions only once in non-toy Python code in two
or more years.


Yet -- scheme does not provide out-of-the-box support for your proposed "let-a-function-implicitly- refer-to-itself" idea. This suggests that the idea itself runs counter to more important aspects of a programming language.<

I see. It's not a very implicit thing, because you have to call a
function anyway, it's just it has a fixed name, like __func__.
I think it doesn't runs counter to Python & D languages (I have asked
for a similar feature in D2 too, and the designers seem to have
accepted the idea, already suggested there by another person in the
past).

Bye,
bearophile
 
S

Steven D'Aprano

In the special case of a self-recursive function, you would like re-
naming "to just work". Out of morbid curiosity, I am compelled to
ask... why do you expect that re-naming anything --- a function, a
class, a module, a variable, anything --- in just one place but not all
the others should ever, under any circumstances, "just work" ?

Patching functions to do pre-processing or post-processing can be a
useful technique, (e.g. for tracing and debugging). This works for
regular functions, but not recursive functions.

.... return [x]
....
listify(42) [42]

_old_listify = listify
def listify(x):
.... print "Calling listify"
.... return _old_listify(x)
....Calling listify
[42]

Note that the call to listify "just works" -- the patch runs once, the
original version of the function runs once, and we get the expected
results without any problems.


Now try the same technique on a recursive function, and it fails:
.... if x > 0: return [x] + rec(x-1)
.... else: return []
....
rec(5) [5, 4, 3, 2, 1]

_old_rec = rec
def rec(x):
.... print "Calling rec"
.... return _old_rec(x)
....Calling rec
Calling rec
Calling rec
Calling rec
Calling rec
Calling rec
[5, 4, 3, 2, 1]

Note too that if you're given an arbitrary function, there's no simple
way of telling whether it's recursive or not. Properly written "self-
reflective" functions using my proposed technique should be immune to
this problem.
 

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

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top