简体   繁体   中英

Can someone help me understand special methods vs normal methods?

What is the difference between using a special method and just defining a normal class method? I was reading this site which lists a lot of them.

For example it gives a class like this.

class Word(str):
    '''Class for words, defining comparison based on word length.'''

    def __new__(cls, word):
        # Note that we have to use __new__. This is because str is an immutable
        # type, so we have to initialize it early (at creation)
        if ' ' in word:
            print "Value contains spaces. Truncating to first space."
            word = word[:word.index(' ')] # Word is now all chars before first space
        return str.__new__(cls, word)

    def __gt__(self, other):
        return len(self) > len(other)
    def __lt__(self, other):
        return len(self) < len(other)
    def __ge__(self, other):
        return len(self) >= len(other)
    def __le__(self, other):
        return len(self) <= len(other)

For each of those special methods why can't I just make a normal method instead, what are they doing different? I think I just need a fundamental explanation that I can't find, thanks.

It is a pythonic way to do this:

word1 = Word('first')
word2 = Word('second')
if word1 > word2:
    pass

instead of direct usage of comparator method

NotMagicWord(str):
    def is_greater(self, other)
        return len(self) > len(other)

word1 = NotMagicWord('first')
word2 = NotMagicWord('second')
if word1.is_greater(word2):
    pass

And the same with all other magic method. You define __len__ method to tell python its length using built-in len function, for example. All magic method will be called implicitly while standard operations like binary operators, object calling, comparision and a lot of other. A Guide to Python's Magic Methods is really good, read it and see what behavior you can give to your objects. It similar to operator overloading in C++, if you are familiar with it.

A method like __gt__ is called when you use comparison operators in your code. Writing something like

value1 > value2

Is the equivalent of writing

value1.__gt__(value2)

Special methods are handled specially by the rest of the Python language. For example, if you try to compare two Word instances with < , the __lt__ method of Word will be called to determine the result.

The magic methods are called when you use < , == , > to compare the objects. functools has a helper called total_ordering that will fill in the missing comparison methods if you just define __eq__ and __gt__ .

Because str already has all the comparison operations defined, it's necessary to add them as a mixin if you want to take advantage of total_ordering

from functools import total_ordering

@total_ordering
class OrderByLen(object):
    def __eq__(self, other):
        return len(self) == len(other)
    def __gt__(self, other):
        return len(self) > len(other)


class Word(OrderByLen, str):
    '''Class for words, defining comparison based on word length.'''

    def __new__(cls, word):
        # Note that we have to use __new__. This is because str is an immutable
        # type, so we have to initialize it early (at creation)
        if ' ' in word:
            print "Value contains spaces. Truncating to first space."
            word = word[:word.index(' ')] # Word is now all chars before first space
        return str.__new__(cls, word)


print Word('cat') < Word('dog')         # False
print Word('cat') > Word('dog')         # False
print Word('cat') == Word('dog')        # True
print Word('cat') <= Word('elephant')   # True
print Word('cat') >= Word('elephant')   # False

"Magic methods" are used by Python to implement a lot of its underlying structure.

For example, let's say I have a simple class to represent an (x, y) coordinate pair:

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

So, __init__ would be an example of one of these "magic methods" -- it allows me to automatically initialize the class by simply doing Point(3, 2) . I could write this without using magic methods by creating my own "init" function, but then I would need to make an explicit method call to initialize my class:

class Point(object):
    def init(self, x, y):
        self.x = x
        self.y = y
        return self


p = Point().init(x, y)

Let's take another example -- if I wanted to compare two point variables, I could do:

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

This lets me compare two points by doing p1 == p2 . In contrast, if I made this a normal eq method, I would have to be more explicit by doing p1.eq(p2) .

Basically, magic methods are Python's way of implementing a lot of its syntactic sugar in a way that allows it to be easily customizable by programmers.

For example, I could construct a class that pretends to be a function by implementing __call__ :

class Foobar(object):
    def __init__(self, a):
        self.a = a

    def __call__(self, b):
        return a + b

f = Foobar(3)
print f(4)  # returns 7

Without the magic method, I would have to manually do f.call(4) , which means I can no longer pretend the object is a function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM