{% raw %}

# Python 魔术方法

> 原文： [http://zetcode.com/python/magicmethods/](http://zetcode.com/python/magicmethods/)

Python 魔术方法教程描述了什么是 Python 魔术方法，并说明了如何使用它们。 在本教程中，我们介绍了一些常见的魔术方法。

## Python 魔术方法

Python 魔术方法是为我们的自定义类添加功能的特殊方法。 它们被双下划线包围（例如`__add __()`）。

Python 中有许多魔术方法。 它们中的大多数用于非常特殊的情况。 我们将提到一些更流行的方法。

## `__add__`方法

`__add__()`方法用于实现加法运算。 在 Python 中，数字不是原始字面值，而是对象。 `num + 4`表达式等效于`num.__add__(4)`。

`add_dict.py`

```py
#!/usr/bin/env python

class MyDict(dict):

    def __add__(self, other):

        self.update(other)
        return MyDict(self)

a = MyDict({'de': 'Germany'})
b = MyDict({'sk': 'Slovakia'})

print(a + b)

```

在示例中，我们有一个自定义字典，该字典使用`__add__()`实现加法运算。

```py
class MyDict(dict):

def __add__(self, other):

    self.update(other)
    return MyDict(self)

```

自定义字典继承自内置`dict`。 `__add__()`方法与`update()`方法添加两个字典，并返回新创建的字典。

```py
a = MyDict({'de': 'Germany'})
b = MyDict({'sk': 'Slovakia'})

```

我们创建两个简单的字典。

```py
print(a + b)

```

我们添加两个字典。

```py
$ ./add_dict.py
{'de': 'Germany', 'sk': 'Slovakia'}

```

这是输出。

## `__init__`和`__str__`方法

`__init__()`方法用于初始化对象。 此方法用于实现对象的构造器。 `__str__()`提供了对象可读的输出。

`init_str.py`

```py
#!/usr/bin/env python

class Person:

    def __init__(self, name, occupation):

        self.name = name
        self.occupation = occupation

    def __str__(self):

        return f'{self.name} is a {self.occupation}'

p = Person('John Doe', 'gardener')
print(p)

```

在示例中，我们有一个`Person`类，具有两个属性：`name`和`occupation`。

```py
def __init__(self, name, occupation):

    self.name = name
    self.occupation = occupation

```

在`__init__()`方法中，我们将实例变量设置为传递给构造器的值。

```py
def __str__(self):

    return f'{self.name} is a {self.occupation}'

```

`__str__()`方法可以很好地输出对象。

```py
$ ./init_str.py
John Doe is a gardener

```

这是输出。

## `__repr__`方法

`__repr__()`方法由内置函数`repr()`调用。 当它求值返回对象的表达式时，在 Python shell 上使用它。

`__str__()`用于提供对象的人类可读版本，`__repr__()`用于提供对象的完整表示。 后者的输出也更适合开发者。

如果缺少`__str__()`实现，则将`__repr__()`方法用作后备。

```py
def __repr__(self):
    return '<{0}.{1} object at {2}>'.format(
      self.__module__, type(self).__name__, hex(id(self)))

```

对象的`__repr__()`方法的默认实现类似于上面的代码。

`repr_ex.py`

```py
#!/usr/bin/env python

class Person:

    def __init__(self, name, occupation):

        self.name = name
        self.occupation = occupation

    def __str__(self):

        return f'{self.name} is a {self.occupation}'

    def __repr__(self):

        return f'Person{{name: {self.name}, occupation: {self.occupation}}}'

p = Person('John Doe', 'gardener')

print(p)
print(repr(p))

```

该示例实现了`__str__()`和`__repr__()`方法。

```py
$ ./repr_ex.py
John Doe is a gardener
Person{name: John Doe, occupation: gardener}

```

这是输出。

## `__len__`和`__getitem__`方法

`__len__()`方法返回容器的长度。 当我们在对象上使用内置的`len()`方法时，将调用该方法。 `__getitem__()`方法定义项目访问（[]）运算符。

`french_deck.py`

```py
#!/usr/bin/env python

import collections
from random import choice

Card = collections.namedtuple('Card', ['suit', 'rank'])

class FrenchDeck:

    ranks = [str(i) for i in range(2, 11)] + list('JQKA')
    suits = ["heart", "clubs", "spades", "diamond"]

    def __init__(self):
        self.total = [Card(suit, rank)
                           for suit in self.suits for rank in self.ranks]

    def __len__(self):
        return len(self.total)

    def __getitem__(self, index):
        return self.total[index]

deck = FrenchDeck()

print(deck[0])
print(len(deck))
print(choice(deck))

```

该方法用于实现法语卡片组。

```py
Card = collections.namedtuple('Card', ['suit', 'rank'])

```

我们使用一个命名的元组来定义一个`Card`类。 `namedtuple`是用于创建元组类的工厂功能。 每张卡都有一套西装和一个等级。

```py
def __len__(self):
    return len(self.total)

```

`__len__()`方法返回卡座（52）中的卡数。

```py
def __getitem__(self, index):
    return self.total[index]

```

`__getitem__()`实现索引操作。

```py
print(deck[0])

```

我们得到卡组的第一张牌。 这称为`__getitem__()`。

```py
print(len(deck))

```

这将调用`__len__()`方法。

```py
$ ./french_deck.py
Card(suit='heart', rank='2')
52
Card(suit='diamond', rank='A')

```

这是输出。

## `__int__`和`__index__`方法

调用`__int__()`方法以实现内置的`int()`函数。 当在切片表达式中使用对象以及内置的`hex()`，`oct()`和`bin()`函数时，`__index__()`方法将类型转换为`int`。

`char_ex.py`

```py
#!/usr/bin/env python

class Char:

    def __init__(self, val):
        self.val = val

    def __int__(self):
        return ord(self.val)

    def __index__(self):
        return ord(self.val)

c1 = Char('a')

print(int(c1))
print(hex(c1))
print(bin(c1))
print(oct(c1))

```

在示例中，我们创建一个自定义的`Char`类，该类实现了`int()`，`hex()`，`bin()`和`oct()`函数。

```py
./char_ex.py
97
0x61
0b1100001
0o141

```

这是输出。

## `__eq __`，`__ lt__`和`__gt__`方法

`__eq__()`实现了`==`运算符。 `__lt__()`实现了`<`运算符，`__gt__()`实现了`>`运算符。

`pouch.py`

```py
#!/usr/bin/env python

import collections

Coin = collections.namedtuple('coin', ['rank'])

# a gold coin equals to two silver and six bronze coins

class Pouch:

    def __init__(self):
        self.bag = []

    def add(self, coin):

        self.bag.append(coin)

    def __eq__(self, other):

        val1, val2 = self.__evaluate(other)

        if val1 == val2:
            return True
        else:
            return False

    def __lt__(self, other):

        val1, val2 = self.__evaluate(other)

        if val1 < val2:
            return True
        else:
            return False

    def __gt__(self, other):

        val1, val2 = self.__evaluate(other)

        if val1 > val2:
            return True
        else:
            return False

    def __str__(self):

        return str(self.bag)

    def __evaluate(self, other):

        val1 = 0
        val2 = 0

        for coin in self.bag:

            if coin.rank == 'g':
                val1 += 6

            if coin.rank == 's':
                val1 += 3

            if coin.rank == 'b':
                val1 += 1

        for coin in other.bag:

            if coin.rank == 'g':
                val2 += 6

            if coin.rank == 's':
                val2 += 3

            if coin.rank == 'b':
                val2 += 1

        return val1, val2

pouch1 = Pouch()

pouch1.add(Coin('g'))
pouch1.add(Coin('g'))
pouch1.add(Coin('s'))

pouch2 = Pouch()

pouch2.add(Coin('g'))
pouch2.add(Coin('s'))
pouch2.add(Coin('s'))
pouch2.add(Coin('b'))
pouch2.add(Coin('b'))
pouch2.add(Coin('b'))

print(pouch1)
print(pouch2)

if pouch1 == pouch2:
    print('Pouches have equal value')

elif pouch1 > pouch2:
    print('Pouch 1 is more valueable than Pouch 2')
else:
    print('Pouch 2 is more valueable than Pouch 1')

```

我们有一个可以容纳金，银和青铜硬币的小袋。 一枚金币等于两个银币和六个铜币。 在示例中，我们使用 Python 魔术方法为`pouch`对象实现了三个比较运算符。

```py
def __eq__(self, other):

    val1, val2 = self.__evaluate(other)

    if val1 == val2:
        return True
    else:
        return False

```

在`__eq__()`方法中，我们首先求值两个小袋的值。 然后我们比较它们并返回布尔结果。

```py
def __evaluate(self, other):

    val1 = 0
    val2 = 0

    for coin in self.bag:

        if coin.rank == 'g':
            val1 += 6

        if coin.rank == 's':
            val1 += 3

        if coin.rank == 'b':
            val1 += 1

    for coin in other.bag:

        if coin.rank == 'g':
            val2 += 6

        if coin.rank == 's':
            val2 += 3

        if coin.rank == 'b':
            val2 += 1

    return val1, val2

```

`__evaluate()`方法计算两个袋的值。 它穿过小袋的硬币，并根据硬币的等级增加一个值。

```py
pouch1 = Pouch()

pouch1.add(Coin('g'))
pouch1.add(Coin('g'))
pouch1.add(Coin('s'))

```

我们创建第一个袋，并在其中添加三个硬币。

```py
if pouch1 == pouch2:
    print('Pouches have equal value')

elif pouch1 > pouch2:
    print('Pouch 1 is more valueable than Pouch 2')
else:
    print('Pouch 2 is more valueable than Pouch 1')

```

我们将小袋与比较运算符进行比较。

## 2D 向量示例

在下面的示例中，我们介绍了几种其他魔术方法，包括`__sub__()`，`__mul__()`和`__abs__()`。

`vector.py`

```py
#!/usr/bin/env python

import math

class Vec2D:

    def __init__(self, x, y):

        self.x = x
        self.y = y

    def __add__(self, other):
        return Vec2D(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vec2D(self.x - other.x, self.y - other.y)

    def __mul__(self, other):
        return self.x * other.x + self.y * other.y

    def __abs__(self):
        return math.sqrt(self.x ** 2 + self.y ** 2)

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

    def __str__(self):
        return f'({self.x}, {self.y})'

    def __ne__(self, other):
        return not self.__eq__(other)  

u = Vec2D(0, 1)
v = Vec2D(2, 3)
w = Vec2D(-1, 1)

a = u + v
print(a)

print(a == w)

a = u - v
print(a)

a = u * v

print(a)
print(abs(u))
print(u == v)
print(u != v)

```

在示例中，我们有一个`Vec2D`类。 我们可以比较，加，减和乘向量。 我们还可以计算向量的长度。

```py
$ ./vector.py
(2, 4)
False
(-2, -2)
3
1.0
False 
True

```

这是输出。

在本教程中，我们使用了 Python 魔术方法。

您可能也对以下相关教程感兴趣： [Python 字符串](/lang/python/strings/)， [Python Jinja 教程](/python/jinja/)和 [Python 教程](/lang/python/)，或列出[所有 Python 教程](/all/#python) 。

{% endraw %}