How do I alias member functions in Python?

23

0

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?

Rainbolt

Posted 2014-05-08T21:03:37.907

Reputation: 6 176

Define your own class, with its method q meaning what replace means in the class your using. – Ypnypn – 2014-05-08T21:07:49.720

@Ypnypn I see. So you alias the class rather than the function. It seems obvious now that you said it. Do you want to post your answer? I'll even expand it with an example when I get around to it, but I don't get any rep for answering my own question. – Rainbolt – 2014-05-08T21:15:45.313

3I'm glad this hasn't been downvoted :) – TheDoctor – 2014-05-08T23:08:01.700

I have literally no idea why this kind of tips question has to CW, too. – Martin Ender – 2014-05-09T22:20:49.367

2

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: http://meta.codegolf.stackexchange.com/q/1555/3808

– Doorknob – 2014-05-10T14:34:45.820

3Now 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. – Doorknob – 2014-05-13T21:58:04.860

1Your 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") – gnibbler – 2014-05-14T01:40:18.473

@gnibbler Wow, you're right. I guess I never actually tested the bad version. I'll modify the question. – Rainbolt – 2014-05-14T12:58:57.513

Answers

28

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'>

MtnViewMark

Posted 2014-05-08T21:03:37.907

Reputation: 4 779

3How did I not see this before? – Rainbolt – 2014-05-14T14:44:32.910

6

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')

Ypnypn

Posted 2014-05-08T21:03:37.907

Reputation: 10 485

5

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)"

gnibbler

Posted 2014-05-08T21:03:37.907

Reputation: 14 170

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. – Rainbolt – 2014-05-14T14:47:28.090

4

You may use the reduce function.

reduce(lambda s,(a,b):s.replace(a,b),[('H','J'),('e','i'),('l','m'),('o','y')],'Hello')

Howard

Posted 2014-05-08T21:03:37.907

Reputation: 23 109

If your answer different from this answer (as far as technique used, not necessarily written in the exact same form)?

– Rainbolt – 2014-05-10T18:54:56.107

1@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). – Howard – 2014-05-10T20:36:12.350

1

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))

TheDoctor

Posted 2014-05-08T21:03:37.907

Reputation: 7 793

I was hoping for an answer that wasn't specific to replace. I just used that as an example. – Rainbolt – 2014-05-08T21:14:42.290

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]) – Justin – 2014-05-09T02:54:27.913

2Doesn'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? – Justin – 2014-05-09T03:32:02.417

1

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')

Justin

Posted 2014-05-08T21:03:37.907

Reputation: 19 757

1No need: str.replace /is/ that lambda! See my answer. – MtnViewMark – 2014-05-14T14:34:31.790

1

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

mbomb007

Posted 2014-05-08T21:03:37.907

Reputation: 21 944