python魔法属性_python 魔术方法(一) 自定义容器类与类属性控制

获取元素 -- __getitem__

__getitem__(self, key)

对于容器来说,获取元素是最重要的操作,魔术方法 __getitem__就完成了这个工作,每当对对象通过[]操作符获取元素时,解释器都会自动调用该魔术方法

import collections

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

class FrenchDeck:

ranks = [str(n) for n in range(2, 11)] + list('JQKA')

suits = 'spades diamonds clubs hearts'.split()

def __init__(self):

self._cards = [Card(rank, suit) for suit in self.suits

for rank in self.ranks]

def __getitem__(self, position):

return self._cards[position]

索引与切片

我们建立了一个纸牌类,有了 __getitem__ 方法,我们就定义了索引操作,所有 dict 通过 [] 可以做的事,我们的纸牌类都可以做到:

>>> deck = FrenchDeck()

>>> deck[0]

Card(rank='2', suit='spades')

>>> deck[-1]

Card(rank='A', suit='hearts')

>>> 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='diamonds'),

Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]

迭代

同时,我们的类也因此变得可以被迭代:

>>> for card in deck:

... print(card)

Card(rank='2', suit='spades')

Card(rank='3', suit='spades')

Card(rank='4', suit='spades')

或者反向迭代:

>>> for card in reversed(deck):

... print(card)

Card(rank='A', suit='hearts')

Card(rank='K', suit='hearts')

Card(rank='Q', suit='hearts')

in 操作

in 操作也同样可以:

>>> Card('Q', 'hearts') in deck

True

>>> Card('7', 'beasts') in deck

False

获取容量 -- __len__

__len__(self)

对于容器类,一个很重要的操作是获取容器中元素的数量 -- len()

我们曾经介绍过 Python 对象的内存结构:

len() 方法被调用时,Python 会自动调用对象的 __len__ 方法

对于内部类型,比如 list、dict、str、bytearray 等,__len__ 方法直接返回 PyVarObject 中的 ob_size 字段,而对于自定义类对象,你就需要去实现 __len__ 方法了

class FrenchDeck:

ranks = [str(n) for n in range(2, 11)] + list('JQKA')

suits = 'spades diamonds clubs hearts'.split()

def __init__(self):

self._cards = [Card(rank, suit) for suit in self.suits

for rank in self.ranks]

def __len__(self):

return len(self._cards)

def __getitem__(self, position):

return self._cards[position]

执行 len(deck) 返回了 52

元素的更改与删除 -- __setitem__ 与 __delitem__

__setitem__(self, key, value)

__delitem__(self, key)

上面我们实现的容器类是不可变的,如果你想要改变或删除其中的元素就会报错:TypeError: 'FrenchDeck' object does not support item assignment

__setiem__ 与 __delitem__ 就是分别在更改容器元素值和删除元素时被自动调用的魔术方法

import collections

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

class FrenchDeck:

ranks = [str(n) for n in range(2, 11)] + list('JQKA')

suits = 'spades diamonds clubs hearts'.split()

def __init__(self):

self._cards = [Card(rank, suit) for suit in self.suits

for rank in self.ranks]

def __getitem__(self, position):

return self._cards[position]

def __setitem__(self, key, value):

self._cards[key] = value

def __delitem__(self, key):

del self._cards[key]

if __name__ == '__main__':

deck = FrenchDeck()

print(deck[0])

deck[0] = Card('4', 'spades')

print(deck[0])

执行展示:Card(rank='2', suit='spades')

Card(rank='4', suit='spades')

容器的迭代 -- __iter__ 与 __reversed__

__iter__(self)

__reversed__(self)

定义 __getitem__ 以后,对象已经可以被循环迭代,但更好的方式是通过 __iter__ 方法返回迭代器来支持迭代

for x in containers 等方式的循环中,解释器会自动调用 __iter__ 方法获取迭代器进行迭代

而有时我们需要调用 python 的内建方法 reversed 来实现反向迭代,解释器就会自动调用 __reversed__ 方法

虽然上文提到,通过 __getitem__ 方法就可以实现上述功能,但迭代器会让这一过程的效率更高

容器元素的包含 -- __contains__ 与 __missing__

__contains__

__contains__(self, item)

当判断元素 in 或者 not in 容器时,python 解释器会自动调用 __contains__ 方法

__missing__

__missing__(self, key)

如果你的类是一个继承自 dict 的字典类,并且你没有实现自己的 __getitem__ 方法,那么当默认的 __getitem__ 方法发现 key 不存在时,就会调用你的 __missing__ 方法了

但是,需要注意的是,如果你自己实现了 __getitem__ 方法,并且没有调用父类的 __getitem__ 方法,那 __missing__ 将永远都不会被调用

这有两种方法可以解决:显式调用

class DictSubclass(dict):

def __getitem__(self, key):

if key not in self.data:

return self.__missing__(key)

return self.data[key]

使用父类 __getitem__

class DictSubclass(dict):

def __getitem__(self, key):

if key not in self.data:

return super(DictSubclass , self).__getitem__(key)

return self.data[key]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值