python中lt方法_Python进阶——如何正确使用魔法方法?(下)

本文详细介绍了Python中的魔法方法,包括比较操作(__cmp__、__eq__、__ne__、__lt__、__gt__)、容器类操作(__setitem__、__getitem__、__delitem__、__len__、__iter__、__contains__、__reversed__)、可调用对象的__call__方法以及序列化的__getstate__和__setstate__方法。通过实例展示了如何利用这些魔法方法自定义对象的行为,如自定义比较逻辑、创建自定义容器类、使实例可调用以及控制序列化过程。

在上一篇文章Python进阶——如何正确使用魔法方法?(上)中,我们主要介绍了关于构造与初始化、类的表示、访问控制这几类的魔法方法,以及它们的使用场景。

这篇文章,我们继续介绍剩下的魔法方法,主要包括:比较操作、容器类操作、可调用对象、序列化。

比较操作

比较操作的魔法方法主要包括以下几种:__cmp__

__eq__

__ne__

__lt__

__gt__

__cmp__

从名字我们就能看出来这个魔法方法的作用,当我们需要比较两个对象时,我们可以定义__cmp__来实现比较操作。class Person(object):

def __init__(self, uid):

self.uid = uid

def __cmp__(self, other):

if self.uid == other.uid:

return 0

if self.uid > other.uid:

return 1

return -1

p1 = Person(1)

p2 = Person(2)

print p1 > p2 # False

print p1 

print p1 == p2 # False

从例子中我们可以看到,比较两个对象的具体逻辑:如果__cmp__返回大于 0 的整数(一般为1),说明 self > other

如果__cmp__返回大于 0 的整数(一般为-1),说明 self < other

如果__cmp__返回 0,说明 self == other

当然,这种比较方式有一定的局限性,如果我有 N 个属性,当比较谁大时,我们想用属性 A 来比较。当比较谁小时,我们想用属性 B 来比较,此时__cmp__就无法很好地实现这个逻辑了,所以它只适用于通用的比较逻辑。那如何实现复杂的比较逻辑?这就需要用到__eq__、__ne__、__lt__、__gt__这些魔法方法了,我们看下面这个例子。# coding: utf8

class Person(object):

def __init__(self, uid, name, salary):

self.uid = uid

self.name = name

self.salary = salary

def __eq__(self, other):

"""对象 == 判断"""

return self.uid == other.uid

def __ne__(self, other):

"""对象 != 判断"""

return self.uid != other.uid

def __lt__(self, other):

"""对象 

return len(self.name) 

def __gt__(self, other):

"""对象 > 判断 根据alary"""

return self.salary > other.salary

p1 = Person(1, 'zhangsan', 1000)

p2 = Person(1, 'lisi', 2000)

p3 = Person(1, 'wangwu', 3000)

print p1 == p1 # uid 是否相同

print p1 != p2 # uid 是否不同

print p2 

print p3 > p2 # salary 比较

__eq__

__eq__我们在上一篇文章已经介绍过,它配合__hash__方法,可以判断两个对象是否相等。但在这个例子中,当判断两个对象是否相等时,实际上我们比较的是uid这个属性。

__ne__

同样地,当需要判断两个对象不相等时,会调用__ne__方法,在这个例子中,我们也是根据uid来判断的。

__lt__

当判断一个对象是否小于另一个对象时,会调用__lt__方法,在这个例子中,我们根据name的长度来做的比较。

__gt__

同样地,在判断一个对象是否大于另一个对象时,会调用__gt__方法,在这个例子中,我们根据salary属性判断。在 Python3 中,__cmp__被取消了,因为它和其他魔法方法存在功能上的重复。

容器类操作

接下来我们来看容器类的魔法方法,主要包括:__setitem__

__getitem__

__delitem__

__len__

__iter__

__contains__

__reversed__

是不是很熟悉?我们在开发中多少都使用到过这些方法。在介绍容器的魔法方法之前,我们首先想一下,Python 中的容器类型都有哪些?是的,Python 中常见的容器类型有:字典

元组

列表

字符串

这些都是容器类型。为什么这么说?因为它们都是「可迭代」的。可迭代是因为,它们都实现了容器协议,也就是我们下面要介绍到的魔法方法。我们看下面这个例子。# coding: utf8

class MyList(object):

"""自己实现一个list"""

def __init__(self, values=None):

# 初始化自定义list

self.values = values or []

def __setitem__(self, key, value):

# 添加元素

self.values[key] = value

def __getitem__(self, key):

# 获取元素

return self.values[key]

def __delitem__(self, key):

# 删除元素

del self.values[key]

def __len__(self):

# 自定义list的元素个数

return len(self.values)

def __iter__(self):

# 可迭代

return self

def next(self):

# 迭代的具体细节

# 如果__iter__返回self 则必须实现此方法

if self._index >= len(self.values):

raise StopIteration()

value = self.values[self._index]

self._index += 1

return value

def __contains__(self, key):

# 元素是否在自定义list中

return key in self.values

def __reversed__(self):

# 反转

return list(reversed(self.values))

# 初始化自定义list

my_list = MyList([1, 2, 3, 4, 5])

print my_list[0]      # __getitem__

my_list[1] = 20       # __setitem__

print 1 in my_list      # __contains__

print len(my_list)      # __len__

print [i for i in my_list]  # __iter__

del my_list[0]              # __del__

reversed_list = reversed(my_list) # __reversed__

print [i for i in reversed_list]  # __iter__

在这个例子中,我们自己实现了一个 MyList 类,在这个类中,定义了很多容器类的魔法方法。这样一来,我们这个 MyList 类就可以像操作普通list一样,通过切片的方式添加、获取、删除、迭代元素了。

__setitem__

当我们执行my_list[1] = 20时,就会调用__setitem__方法,这个方法主要用于向容器内添加元素。

__getitem__

当我们执行my_list[0]时,就会调用__getitem__方法,这个方法主要用于从容器中读取元素。

__delitem__

当我们执行del my_list[0]时,就会调用__delitem__方法,这个方法主要用于从容器中删除元素。

__len__

当我们执行len(my_list)时,就会调用__len__方法,这个方法主要用于读取容器内元素的数量。

__iter__

这个方法我们需要重点关注,为什么我们可以执行[i for i in my_list]?就是因为我们定义了__iter__。这个方法的返回值可以有两种:返回iter(obj):代表使用obj对象的迭代协议,一般obj是内置的容器对象

返回self:代表迭代的逻辑由本类来实现,此时需要重写next方法,实现自定义的迭代逻辑

在这个例子中,__iter__返回的是self,所以我们需要定义next方法,实现自己的迭代细节。next方法使用一个索引变量,用于记录当前迭代的位置,这个方法每次被调用时,都会返回一个元素,当所有元素都迭代完成后,这个方法会返回StopIteration异常,此时for会停止迭代。在 Python3 中,已不再使用 next 方法,取而代之的是__next__。

__contains__

从名字也能看出来,这个方法是在执行1 in my_list时触发,用于判断某个元素是否存在于容器中。

__reversed__

这个方法在执行reversed(my_list)时触发,用于反转容器的元素,具体的反转逻辑我们也可以自己实现。

可调用对象

了解了容器类魔法方法,我们接着来看可调用对象的魔法方法,这个魔法方法只有一个:__call__。我们看下面这个例子。# coding: utf8

class Circle(object):

def __init__(self, x, y):

self.x = x

self.y = y

def __call__(self, x, y):

self.x = x

self.y = y

c = Circle(10, 20)  # __init__

print c.x, c.y     # 10 20

c(100, 200)         # 调用instance() 触发__call__

print c.x, c.y      # 100 200

仔细看这个例子,我们首先初始化一个Circle实例c,此时会调用__init__方法,这个很好理解。但是,我们对于实例c又做了调用c(100, 200),注意,此时的c是一个实例对象,当我们这样执行时,其实它调用的就是__call__。这样一来,我们就可以把实例当做一个方法来执行。如果不好理解,你可以多看几遍这个例子,理解一下。也就是说,Python 中的实例,也是可以被调用的,通过定义__call__方法,就可以传入自定义参数实现自己的逻辑。这个魔法方法通常会用在类实现一个装饰器、元类等场景中,当你遇到这个魔法方法时,你能理解其中的原理就可以了。

序列化

我们知道 Python 提供了序列号模块pickle,当我们使用这个模块序列化一个实例时,也可以通过魔法方法来实现自己的逻辑,这些魔法方法包括:__getstate__

__setstate__

我们来看下面的例子。# coding: utf8

class Person(object):

def __init__(self, name, age, birthday):

self.name = name

self.age = age

self.birthday = birthday

def __getstate__(self):

# 执行 pick.dumps 时 忽略 age 属性

return {

'name': self.name,

'birthday': self.birthday

}

def __setstate__(self, state):

# 执行 pick.loads 时 忽略 age 属性

self.name = state['name']

self.birthday = state['birthday']

person = Person('zhangsan', 20, date(2017, 2, 23))

pickled_person = pickle.dumps(person) # __getstate__

p = pickle.loads(pickled_person) # __setstate__

print p.name, p.birthday

print p.age # AttributeError: 'Person' object has no attribute 'age'

__getstate__

在这个例子中,我们首先初始了Person对象,其中包括 3 个属性:name、age、birthday。当我们调用pickle.dumps(person)时,__getstate__方法就会被调用,在这里我们忽略了Person对象的age属性,那么person在序列化时,就只会对其他两个属性进行保存。

__setstate__

同样地,当我们调用pickle.loads(pickled_person)时,__setstate__会被调用,其中入参就是__getstate__返回的结果。在__setstate__方法,我们从入参中取得了被序列化的dict,然后从dict中取出对应的属性,就达到了反序列的效果。

其他魔法方法

好了,以上介绍的这些,就是我们平时遇到比较多的魔法方法。剩下的魔法方法还有很多,主要包括数值处理、算术操作、反射算术操作、增量赋值、类型转换、反射这几类,由于我们在开发中很少会见到,这里就不再过多介绍了,当遇到时,我们直接查阅文档了解即可。

总结

这篇文章,我们主要介绍了关于比较操作、容器类、可调用对象、序列化等魔法方法。其中,比较操作的魔法方法,可以用于自定义实例的比较逻辑。容器类魔法方法,可以帮我们实现一个自定义的容器类,然后我们就可以像操作list、dict那样,方便地去获取容器里的元素、迭代数据等等。可调用对象魔法方法,可以把一个实例当做方法来调用。序列化的魔法方法,可以修改一个实例的序列化和反序列化逻辑。Python 的魔法方法正如它的名字一样,如果使用得当,我们的类就像被添加了魔法一样,变得更易用。我们可以使用这些魔法方法,帮我们实现一些复杂的功能,例如装饰器、元类等等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值