Python 前后带有两个下划线的方法简介

简介

魔术方法(magic method)是特殊方法的昵称,在Python中的特殊方法,一般都是使用诸如__xxx__(前后两个下划线,中间是方法名)的命名方式,在书里有个名词也可以形容它,比如__getitem__,叫做“双下—getitem” (dunnder-getitem)

万事开头难

难以用概念去概括Python特殊方法的作用,最简单的方法就是用例子说明。

很多人都会选择使用Python作为快速开发工具,而特殊方法是属于“快速”这个性质。

在Python中,要拿到一个集合的某个元素,可以使用对应的引索进行取值,比如list[key],这背后利用的是__getitem__方法,为了拿到my_list[key]的值,解释器实际上会调用my_list.getitem(key)。

Python也是面向对象编程语言,对于求一个集合的长度使用len(list)而不是list.len()会感觉有点奇怪,背后就是特殊方法的作用,调用了list.len()方法,和面向对象完全符合,而且还起到简化的作用,变得更加通俗易懂。

一个完整的例子

import collections

Card = collections.namedtuple('Card',['rank', 'suit'])   # 具名元组

class FrenchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')  # 牌数 
    suits = 'spades hearts clubs diamonds'.split()        # 牌色

    def __init__(self):                            # 初始化(构造函数)
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]

    def __len__(self):                             # 用len取长度的特殊方法
        return len(self._cards)

    def __getitem__(self, position):               # 用引索取值的特殊方法
        return self._cards[position]

运行及结果:

>>> deck = FrenchDeck()
>>> len(deck)
52
>>> deck[0]
Card(rank='2', suit='spades')

随机抽牌

那么我们可以使用random模块里面的一个方法随机抽取一张牌

>>> from random import choice
>>> choice(deck)
Card(rank='2', suit='clubs')
>>> choice(deck)
Card(rank='J', suit='clubs')

切片操作

既然我们实现了引索中括号[]操作,那么我们也可以使用切片操作:

>>> deck[:3]
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), 
Card(rank='4', suit='spades')]
>>> deck[12::13]
[Card(rank='A', suit='spades'), Card(rank='A', suit='hearts'), 
Card(rank='A', suit='clubs'), Card(rank='A', suit='diamonds')]

迭代操作

另外,仅仅实现了__getitem__方法,那么也变成了可迭代的了:

>>> for card in deck: # 反向迭代也可以 reversed(deck)
    print(card)

Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
···
Card(rank='Q', suit='diamonds')
Card(rank='K', suit='diamonds')
Card(rank='A', suit='diamonds')

in运算符

如果一个集合没有实现__contains__方法,那么in运算符会按顺序做一次迭代搜索。

>>> Card('A', 'hearts') in deck
True
>>> Card('B', 'hearts') in deck
False

排序

按照扑克牌的大小,2最小,A最大,同时要加上花色的大小判定,从大到小排序:黑桃、红桃、梅花、方块。

suit_values = dict(spades = 3, hearts = 2, clubs = 1, diamonds = 0)
def spades_high(card):
    rank_value = FrenchDeck.ranks.index (card.rank)
    return rank_value * len(suit_values) + suit_values[card.suit]

运行:

>>> for card in sorted(deck, key = spades_high):
    print(card)

Card(rank='2', suit='diamonds')
Card(rank='2', suit='clubs')
Card(rank='2', suit='hearts')
Card(rank='2', suit='spades')
···
Card(rank='A', suit='diamonds')
Card(rank='A', suit='clubs')
Card(rank='A', suit='hearts')
Card(rank='A', suit='spades')
  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值