23
\$\begingroup\$

In Python, one can save bytes by aliasing functions that are used repeatedly. For example:

r=range
a=r(100)
b=r(200)
c=r(300)

However, when the functions are member functions together, I don't know how to alias them in a way that allows chaining. For example:

s='Hello'

// Plain code
s=s.replace('H','J').replace('e','i').replace('l','m').replace('o','y')

// What I am trying to do
q=replace
s=s.q('H','J').q('e','i').q('l','m').q('o','y')

Obviously, what I am trying to do is not valid. And neither is this:

q=s.replace
s=q('H','J') // Replaces the 'H' in 'Hello'
s=q('e','i') // Replaces the 'e' in 'Hello'... and the J is gone.
s=q('l','m')
s=q('o','y')

Is there a another way to alias member functions and chained functions that saves characters?

\$\endgroup\$
  • \$\begingroup\$ Define your own class, with its method q meaning what replace means in the class your using. \$\endgroup\$ – Ypnypn May 8 '14 at 21:07
  • 3
    \$\begingroup\$ I'm glad this hasn't been downvoted :) \$\endgroup\$ – TheDoctor May 8 '14 at 23:08
  • 2
    \$\begingroup\$ I've unwikied all the answers for now, but we haven't reached a strong enough consensus to call for unwiki-ing this and similar questions. See also: meta.codegolf.stackexchange.com/q/1555/3808 \$\endgroup\$ – Doorknob May 10 '14 at 14:34
  • 3
    \$\begingroup\$ Now that the aforementioned meta discussion is semi-official (or at least most of us agree), I've gone ahead and removed the wiki on this post. \$\endgroup\$ – Doorknob May 13 '14 at 21:58
  • 1
    \$\begingroup\$ Your last version doesn't work. q is bound to the replace method of that specific str instance. Also, remember you can do single char replacements with "Hello".replace(*"HJ") \$\endgroup\$ – gnibbler May 14 '14 at 1:40
27
\$\begingroup\$

No problemo! You can alias a method, but you have to know how to use it:

>>> r=str.replace
>>> a='hello'
>>> r(r(r(r(a,'h','j'),'e','i'),'l','m'),'o','y')
'jimmy'

The key is that you have to pass self explicitly, because the alias is a kind of function that takes an extra argument that takes self:

>>> type(r)
<type 'method_descriptor'>
\$\endgroup\$
  • 3
    \$\begingroup\$ How did I not see this before? \$\endgroup\$ – Rainbolt May 14 '14 at 14:44
6
\$\begingroup\$

Define your own class, with a shorter method name.

For example, if you're using the method replace() belonging to the String class, you could make your own class S have a method called q which does the same thing.

Here is one implementation:

class m(str):
 def q(a,b,c):return m(a.replace(b,c))

Here is a much better implementation:

class m(str):q=lambda a,b,c:m(a.replace(b,c))

Use it like so:

s="Hello"
s=m(s).q('H','J').q('e','i').q('l','m').q('o','y')
\$\endgroup\$
4
\$\begingroup\$

You may use the reduce function.

reduce(lambda s,(a,b):s.replace(a,b),[('H','J'),('e','i'),('l','m'),('o','y')],'Hello')
\$\endgroup\$
  • \$\begingroup\$ If your answer different from this answer (as far as technique used, not necessarily written in the exact same form)? \$\endgroup\$ – Rainbolt May 10 '14 at 18:54
  • 1
    \$\begingroup\$ @Rusher It is different. E.g. the number of invocations is hard-coded in the linked answer while here it is given only by the length of the second argument (which can be any iterator). \$\endgroup\$ – Howard May 10 '14 at 20:36
4
\$\begingroup\$

This is a few characters shorter anyway

j=iter('HJeilmoy')
for i in j:s=s.replace(i,next(j))

even shorter for a small number of replacements is

for i in['HJ','ei','lm','oy']:s=s.replace(*i)

of course this just covers one particular case. However code golf is all about finding those special cases that can save you bytes.

It's possible to write a wrapper function that handles the general case, but the code will be too large to have a place in most code golf challenges.

You instead need to think "I can do this transformation efficiently (strokewise) with str.replace. Can I shift the internal representation of my solution to take advantage of that? (without wasting so many strokes to negate the advantage)"

\$\endgroup\$
  • \$\begingroup\$ I do like this answer because it describes good habits when golfing and definitely solves the specific example presented. I accepted another answer because it is applicable to the more general case. \$\endgroup\$ – Rainbolt May 14 '14 at 14:47
1
\$\begingroup\$

If you're doing a lot of replaces, you could do this:

s=''.join({'H':'J','e':'i','l':'m','o':'y'}[a] for a in list(s))
\$\endgroup\$
  • \$\begingroup\$ I was hoping for an answer that wasn't specific to replace. I just used that as an example. \$\endgroup\$ – Rainbolt May 8 '14 at 21:14
  • \$\begingroup\$ This can only replace 1 char at a time, but it can insert multi-char replacements. And this is not fully golfed (remove the space after [a]) \$\endgroup\$ – Justin May 9 '14 at 2:54
  • 2
    \$\begingroup\$ Doesn't the [a] have to be replaced with .get(a,a) (otherwise, we get some Key Error thing)? And why have list(s) instead of s? \$\endgroup\$ – Justin May 9 '14 at 3:32
1
\$\begingroup\$

How about defining a lambda function?

r=lambda s,a,b:s.replace(a,b)

s=r(r(r(r(s,'H','J'),'e','i'),'l','m'),'o','y')
\$\endgroup\$
  • 1
    \$\begingroup\$ No need: str.replace /is/ that lambda! See my answer. \$\endgroup\$ – MtnViewMark May 14 '14 at 14:34
0
\$\begingroup\$

If your replacements don't need to be chained, you can use `str.translate'. The numbers are the ASCII ordinals. Using it this way requires Python 3:

print("Hello".translate({72:74,101:105,108:109,111:121}))

Try it online

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.