Param decorator - can you suggest improvements

M

Mark Carter

I thought it would be interesting to try to implement Scheme SRFI 39 (Parameter objects) in Python.

The idea is that you define a function that returns a default value. If youcall that function with no arguments, it returns the current default. If you call it with an argument, it resets the default to the value passed in. Here's a working implementation:

# http://www.artima.com/weblogs/viewpost.jsp?thread=240845

from functools import wraps

class Param(object):
def __init__(self, default):
self.default = default
#self.func = func

def __call__(self, func, *args):
@wraps(func)
def wrapper(*args):
#print 'calling hi'
if len(args) >0:
self.default = args[0]
#print len(args), ' ', args
return self.default # self.func(*args)
return wrapper


@Param(42)
def hi(newval):
pass

print hi() # 42
print hi(12) # 12
print hi() # 12
hi(13) # sets it to 13
print hi() # 13


Can anyone suggest a better implementation?
 
S

Steven D'Aprano

I thought it would be interesting to try to implement Scheme SRFI 39
(Parameter objects) in Python.

The idea is that you define a function that returns a default value. If
you call that function with no arguments, it returns the current
default. If you call it with an argument, it resets the default to the
value passed in. Here's a working implementation: [...]
Can anyone suggest a better implementation?

I don't like the decorator version, because it requires creating a do-
nothing function that just gets thrown away. He's my version, a factory
function that takes two arguments, the default value and an optional
function name, and returns a Param function:

def param(default, name=None):
SENTINEL = object()
default = [default]
def param(arg=SENTINEL):
if arg is SENTINEL:
return default[0]
else:
default[0] = arg
return arg
if name is not None:
param.__name__ = name
return param


In Python 3, it's even nicer, although no shorter:

def param(default, name=None):
SENTINEL = object()
def param(arg=SENTINEL):
nonlocal default
if arg is SENTINEL:
return default
else:
default = arg
return arg
if name is not None:
param.__name__ = name
return param
 
S

Steven D'Aprano

I thought it would be interesting to try to implement Scheme SRFI 39
(Parameter objects) in Python.

The idea is that you define a function that returns a default value.
If you call that function with no arguments, it returns the current
default. If you call it with an argument, it resets the default to the
value passed in. Here's a working implementation: [...]
Can anyone suggest a better implementation?

I don't like the decorator version, because it requires creating a do-
nothing function that just gets thrown away. He's my version, a factory
function that takes two arguments, the default value and an optional
function name, and returns a Param function: [...]

This, or something like this, is very old:

sentinel = object()
class Magic:
def __init__(self, value):
self.value = value
def __call__(self, value=sentinel):
if value != sentinel:
self.value = value
return self.value

There's not really any magic in that :)

Better to use "if value is not sentinel" rather than != because the
caller might provide a custom object that compares equal to sentinel.

Also you should name it SENTINEL, or even _SENTINEL, to indicate that it
is (1) a constant, and (2) a private variable.

It's not a function, nor a decorator, but it behaves like a function.

A callable, so-called because you can call it :)

I believe that C++ calls it a "functor", not to be confused with what
Haskell calls a functor, which is completely different.
 

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,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top