Using closures and partial functions to eliminate redundant code

M

Matthew Wilson

I wrote some code to create a user and update a user on a remote box by
sending emails to that remote box. When I was done, I realized that my
create_user function and my update_user function were effectively
identical except for different docstrings and a single different value
inside:

### VERSION ONE

def create_user(username, userpassword, useremail):
"Send an email that will create a user in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="CREATE", body=email_body)


def update_user(username, userpassword, useremail):
"Send an email that will update a user's password in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="UPDATE", body=email_body)

### END

Then I came up with this approach to avoid all that redundant text:

### VERSION TWO

def _f(mode):

if mode not in ("create", "update"):
raise ValueError("mode must be create or update!")

def _g(username, userpassword, useremail):

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject=mode.upper(), body=email_body)

# Seems goofy, but other ways are there?

docstrings = {'create': "Send an email that will create a user in the remote system.",
'update': "Send an email that will update a user's password in the remote system."}

_g.__doc__ = docstrings[mode]

return _g

# Then I created my functions like this:

v2_create_user = _f("create")
v2_update_user = _f("update")

### END


Finally, I came up with this approach:

### VERSION THREE

from functools import partial

def _h(mode, username, userpassword, useremail):

if mode not in ("create", "update"):
raise ValueError("mode must be create or update!")

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject=mode.upper(), body=email_body)

# I can't figure out how to set up the docstring on these.

v3_create_user = partial(_h, mode="create")
v3_update_user = partial(_h, mode="update")

### END

I'm interested to hear how other people deal with really similar code.
The similarity just bugs me. However, I wonder if using stuff like
closures or partial function application is needlessly showy.

Also, I hope anyone here can help me figure out how to attach a
meaningful docstring for my version three code.

Thanks in advance!


Matt
 
P

Paul McGuire

I wrote some code to create a user and update a user on a remote box by
sending emails to that remote box. When I was done, I realized that my
create_user function and my update_user function were effectively
identical except for different docstrings and a single different value
inside:

### VERSION ONE

def create_user(username, userpassword, useremail):
"Send an email that will create a user in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="CREATE", body=email_body)

def update_user(username, userpassword, useremail):
"Send an email that will update a user's password in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="UPDATE", body=email_body)

### END

Then I came up with this approach to avoid all that redundant text:

### VERSION TWO

def _f(mode):

if mode not in ("create", "update"):
raise ValueError("mode must be create or update!")

def _g(username, userpassword, useremail):

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject=mode.upper(), body=email_body)

# Seems goofy, but other ways are there?

docstrings = {'create': "Send an email that will create a user in the remote system.",
'update': "Send an email that will update a user's password in the remote system."}

_g.__doc__ = docstrings[mode]

return _g

# Then I created my functions like this:

v2_create_user = _f("create")
v2_update_user = _f("update")

### END

Finally, I came up with this approach:

### VERSION THREE

from functools import partial

def _h(mode, username, userpassword, useremail):

if mode not in ("create", "update"):
raise ValueError("mode must be create or update!")

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject=mode.upper(), body=email_body)

# I can't figure out how to set up the docstring on these.

v3_create_user = partial(_h, mode="create")
v3_update_user = partial(_h, mode="update")

### END

I'm interested to hear how other people deal with really similar code.
The similarity just bugs me. However, I wonder if using stuff like
closures or partial function application is needlessly showy.

Also, I hope anyone here can help me figure out how to attach a
meaningful docstring for my version three code.

Thanks in advance!

Matt

Haven't used partial yet, but I'm very interested. Of course, I think
this will work just as well:

def make_method(mode,docstring):
def _inner(username, userpassword, useremail):
# implementation of common routine code goes here
# note: no need to clutter arg list with 'mode' now
_inner.__name__ = mode + "_user"
_inner.__doc__ = docstring
return _inner

Does this not work?

v3_create_user = partial(_h, mode="create")
v3_create_user.__doc__ = "Send an email that will create a user in
the remote system."
v3_update_user = partial(_h, mode="update")
v3_update_user.__doc__ = "Send an email that will update a user's
password in the remote system."


-- Paul
 
T

timaranz

I wrote some code to create a user and update a user on a remote box by
sending emails to that remote box. When I was done, I realized that my
create_user function and my update_user function were effectively
identical except for different docstrings and a single different value
inside:

### VERSION ONE

def create_user(username, userpassword, useremail):
"Send an email that will create a user in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="CREATE", body=email_body)

def update_user(username, userpassword, useremail):
"Send an email that will update a user's password in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="UPDATE", body=email_body)

### END

Then I came up with this approach to avoid all that redundant text:

### VERSION TWO

def _f(mode):

if mode not in ("create", "update"):
raise ValueError("mode must be create or update!")

def _g(username, userpassword, useremail):

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject=mode.upper(), body=email_body)

# Seems goofy, but other ways are there?

docstrings = {'create': "Send an email that will create a user in the remote system.",
'update': "Send an email that will update a user's password in the remote system."}

_g.__doc__ = docstrings[mode]

return _g

# Then I created my functions like this:

v2_create_user = _f("create")
v2_update_user = _f("update")

### END

Finally, I came up with this approach:

### VERSION THREE

from functools import partial

def _h(mode, username, userpassword, useremail):

if mode not in ("create", "update"):
raise ValueError("mode must be create or update!")

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject=mode.upper(), body=email_body)

# I can't figure out how to set up the docstring on these.

v3_create_user = partial(_h, mode="create")
v3_update_user = partial(_h, mode="update")

### END

I'm interested to hear how other people deal with really similar code.
The similarity just bugs me. However, I wonder if using stuff like
closures or partial function application is needlessly showy.

Also, I hope anyone here can help me figure out how to attach a
meaningful docstring for my version three code.

Thanks in advance!

Matt

Without using partials, I would do something like

def _create_or_update_user(mode, username, userpassword, useremail)
blah, blah, blah

def update_user(username, userpassword, useremail):
'update doc string'
return _create_or_update_user('update', username, userpassword,
useremail)

def create_user(username, userpassword, useremail):
'create doc string'
return _create_or_update_user('create', username, userpassword,
useremail)

Cheers
Tim
 
G

Gabriel Genellina

I wrote some code to create a user and update a user on a remote box by
sending emails to that remote box. When I was done, I realized that my
create_user function and my update_user function were effectively
identical except for different docstrings and a single different value
inside:

I would have used an object with two methods... But this is just another
example of the closure/object dichotomy. Some enlightment:
http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html
 
B

Bruno Desthuilliers

Matthew Wilson a écrit :
I wrote some code to create a user and update a user on a remote box by
sending emails to that remote box. When I was done, I realized that my
create_user function and my update_user function were effectively
identical except for different docstrings and a single different value
inside:

### VERSION ONE

def create_user(username, userpassword, useremail):
"Send an email that will create a user in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="CREATE", body=email_body)


def update_user(username, userpassword, useremail):
"Send an email that will update a user's password in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="UPDATE", body=email_body)

### END
(snip)

Finally, I came up with this approach:

### VERSION THREE

from functools import partial

def _h(mode, username, userpassword, useremail):

if mode not in ("create", "update"):
raise ValueError("mode must be create or update!")

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject=mode.upper(), body=email_body)

# I can't figure out how to set up the docstring on these.

v3_create_user = partial(_h, mode="create")
v3_update_user = partial(_h, mode="update")

### END

I'm interested to hear how other people deal with really similar code.
Depends.

The similarity just bugs me. However, I wonder if using stuff like
closures or partial function application is needlessly showy.

Not necessarily, but in this case, it's just overkill IMHO - I'd just
have factored out the common code:

def _build_email(username, userpassword, useremail):
""" builds the email body used by create_uer and update_user """
return """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)


def create_user(username, userpassword, useremail):
"Send an email that will create a user in the remote system."
send_email(subject="CREATE",
body=_build_email(username, userpassword, useremail)
)


def update_user(username, userpassword, useremail):
"Send an email that will update a user's password in the remote
system."
send_email(subject="UPDATE",
body=_build_email(username, userpassword, useremail)
)



Now there are of course cases where either closures and/or partials are
the right thing to do.
Also, I hope anyone here can help me figure out how to attach a
meaningful docstring for my version three code.

Didn't try, but what about:
p = partial(func, arg)
p.__doc__ = "yadda yadda"
 
P

Paul Hankin

I wrote some code to create a user and update a user on a remote box by
sending emails to that remote box. When I was done, I realized that my
create_user function and my update_user function were effectively
identical except for different docstrings and a single different value
inside:

### VERSION ONE

def create_user(username, userpassword, useremail):
"Send an email that will create a user in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="CREATE", body=email_body)

def update_user(username, userpassword, useremail):
"Send an email that will update a user's password in the remote system."

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)

# send it.
send_email(subject="UPDATE", body=email_body)

If you don't mind what order the lines of your email appear in, you
might get more reuse out of something like this. As other's have said
partial isn't needed here.

def send_command_email(subject, **kwargs):
"""Send an email to the with the given subject, and lines of the
body of the form A = B for each keyword argument."""
email_body = '\n'.join('%s = %s' % (k, kwargs[k]) for k in kwargs)
send_email(subject=subject, body='\n' + email_body + '\n'

def create_user(name, password, email):
"Create a user by sending an email to the server"
send_command_email('CREATE', USERNAME=name,
USERPASSWORD=password, USEREMAIL=email)

def update_user(name, password, email):
"Update a user's details by sending an email to the server"
send_command_email('UPDATE', USERNAME=name,
USERPASSWORD=password, USEREMAIL=email)
 
B

Bjoern Schliessmann

Matthew said:
I'm interested to hear how other people deal with really similar
code.
The similarity just bugs me. However, I wonder if using stuff
like closures or partial function application is needlessly showy.

ACK -- but not because it's showy, but because it may be more
error-prone and less readable. I'd often use an approach like this:

def create_user(username, userpassword, useremail, create = False):
"""
Send an email that will update a user in the remote system.
If create evaluates to True, don't update the user, but create
her instead.
"""
if not create:
subject = "UPDATE"
else:
subject = "CREATE"

# Build email
email_body = """
USERNAME = %s
USERPASSWORD = %s
USEREMAIL = %s
""" % (username, userpassword, useremail)
# send it.
send_email(subject=subject, body=email_body)

Regards,


Björn
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top