27
\$\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\$
8
  • \$\begingroup\$ Define your own class, with its method q meaning what replace means in the class your using. \$\endgroup\$
    – Ypnypn
    May 8, 2014 at 21:07
  • 4
    \$\begingroup\$ I'm glad this hasn't been downvoted :) \$\endgroup\$
    – TheDoctor
    May 8, 2014 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, 2014 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, 2014 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, 2014 at 1:40

7 Answers 7

34
\$\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\$
1
  • 3
    \$\begingroup\$ How did I not see this before? \$\endgroup\$
    – Rainbolt
    May 14, 2014 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\$
5
\$\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\$
1
  • \$\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, 2014 at 14:47
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\$
2
  • \$\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, 2014 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, 2014 at 20:36
2
\$\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\$
3
  • \$\begingroup\$ I was hoping for an answer that wasn't specific to replace. I just used that as an example. \$\endgroup\$
    – Rainbolt
    May 8, 2014 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, 2014 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, 2014 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
  • 1
    \$\begingroup\$ No need: str.replace /is/ that lambda! See my answer. \$\endgroup\$ May 14, 2014 at 14:34
1
\$\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 and acknowledge that you have read and understand our privacy policy and code of conduct.

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