4

I am reading python source code: https://hg.python.org/cpython/file/2.7/Lib/collections.py#l621

 def __repr__(self):

        if not self:

            return '%s()' % self.__class__.__name__

        items = ', '.join(map('%r: %r'.__mod__, self.most_common()))

        return '%s({%s})' % (self.__class__.__name__, items)

form the doc:

operator.mod(a, b)
operator.__mod__(a, b)¶
Return a % b.

This is right as i think,

But why '%r: %r'.__mod__ is right?

4
  • 1
    Because __mod__ implements the string format property we are accustomed to e.g. '%r: %r' % (5,2) is implemented via __mod__. It's just operator overloading, I believe. Commented Apr 6, 2016 at 5:40
  • It calls old string formatting operator "%r: %r" % (o1, o2) for each (that's what map does) pair yield from self.most_common(). Commented Apr 6, 2016 at 5:40
  • @Rogalski i am not sure %r: %r'.__mod__, i never see this usage. Commented Apr 6, 2016 at 5:42
  • @BlackMamba I've explained why its __mod__ and not __mod__() in my answer. Commented Apr 6, 2016 at 5:46

3 Answers 3

5

Why Strings Have __mod__

__mod__ implements the behaviour of the % operator in Python. For strings, the % operator is overloaded to give us string formatting options. Where usually a % b would force the evaluation of a mod b if a and b are numbers, for strings, we can change the behaviour of % so that a % b actually inserts the elements of b into a if a is a string.

The way operator overloading works in Python is that each infix operator symbol - +,-,*,/, etc. (and, as of Python 3.5, the matrix multiplication operator @) - corresponds to a specific method in the base definition of the class it's being called on. For +, it is __add__(), for example. For %, it is __mod__(). We can overload these methods by simply redefining them within a class.

If I have class Foo, and Foo implements a member function __add__(self, other), I can potentially make Foo() + bar behave very differently than what the usual definition of + is.

In other words, the string formatting technique

'%s: %s' % (5,2)

in Python actually calls

'%s: %s'.__mod__((5,2))

under the hood, where __mod__ is defined for objects belonging to class string. The way __mod__() is implemented for strings yields, in this case, just 5: 2, rather than the ridiculous interpretation of '%s : %s' mod (5,2)


Why __mod__ in map and not __mod__()

In the specific case of map('%r: %r'.__mod__, self.most_common()), what's happening is that the function pointer (for want of a better word - note that Python doesn't have pointers, but it doesn't hurt to think in that way for a moment) __mod__ is being applied to each of the elements in self.most_common(), rather than the function __mod__().

This is no different than doing, say, map(int, "52"). We don't pass the function invocation int(), we pass a reference to the function as int and expect the function to be invoked by map with the second arguments to map. i.e. that int() will be invoked over each element of "52".

We can't do map('%r: %r'.__mod__(), self.most_common()) for exactly this reason. The function '%r: %r'.__mod__() would be invoked without the appropriate parameters passed in and return an error - what we want instead is a reference to the function __mod__() than we can deference and invoke whenever we like, which is accomplished by calling __mod__.


A C++ Analogy

The behaviour of __mod__ versus __mod__() is really no different than how function pointers work in C++: a function pointer for foo() is denoted by just foo i.e. without the parentheses. Something analogous - but not quite the same - happens here. I introduce this here because it may make the distinction clearer, because on the surface pointers look very similar to what is happening and introducing pointers leads to a fairly familiar mode of thinking which is good enough for this specific purpose.

In C++, we can pass function pointers to other functions and introduce a form of currying - you can then invoke the function pointer on elements through regular foo() syntax inside another function, for example. In Python, we don't have pointers - we have wrapper objects that can reference the underlying memory location (but prevent raw access to it). For our purposes, though, the net effect is the same. @Bukuriu explores the difference in the comments.

Basically, __mod__() forces an evaluation with no parameters; __mod__ returns a pointer to __mod__() than can then be invoked by another function on suitable parameters. Internally, that is what map does: take a function pointer (again, this is an analogy), and then deference and evaluate it on another element.

You can see this yourself: calling '%s'.__mod__ returns

<method-wrapper '__mod__' of str object at 0x7f92ed464690>

i.e. a wrapper object with a reference to the memory address to the function. Meanwhile, calling '%s'.__mod__() returns an error:

TypeError: expected 1 arguments, got 0

because the extra parentheses invoked an evaluation of __mod__ and found there were no arguments.

Sign up to request clarification or add additional context in comments.

3 Comments

Python doesn't have pointers. Also note that s.__mod__ is s.__mod__ is false. Everytime you reference the __mod__ attribute a new method-wrapper object is created and returned. I wouldn't mention C++ because 1) the OP didn't mention it 2) It's completely different from Python and it will only create confusion 3) You are giving false information based on C++ that simply isn't true in python, like that there exist function pointers etc. which is false.
@Bakuriu I know Python doesn't have pointers. The way I've worded this answer, I think, makes it clear I was drawing an analogy. But you're right, I'll make that more explicit.
@Bakuriu Edited. I disagree that bringing C++ into the mix adds confusion - the most popular flavour of Python is CPython, after all - but you're right, making the distinction between Python and C++ is very crucial here. Let me know if you have other suggestions. :)
2

As in http://rafekettler.com/magicmethods.html says

__mod__(self, other)
    Implements modulo using the % operator.

This means when you do string formating '%s' % '123' you do '%s'.__mod__('123')

Comments

1

Let's break this case down.

Essential parts of this complex line are:

seq = self.most_common()
string_representations = map('%r: %r'.__mod__, seq)
items = ', '.join(string_representations)

First line calls Counters method to retrieve top counts from dictionary. Third line joins string representations to single comma-separated string. Both are fairly trivial.

Second line: - calls map - which tells us it calls some function for each element in seq - first argument of map defines function as '%r: %r'.__mod__

We know that operator overloading is done by redefining __magic_methods__ in class declaration. Strings happens to define __mod__ as interpolation operation.

Also, we know that most of operations are just syntactic sugars around those magic methods.

What happens here is magic method being referred explicitly instead of via a % b syntactic sugar. Or, from different perspective, underlining implementation detail is used perform operation instead of standard form.

Second line is roughy equivalent to:

string_representations = ['%r: %r' % o for o in seq]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.