一、new方法
init()是初始化方法,new()方法是构造方法,创建一个新的对象
实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法
new()必须要有返回值,返回实例化出来的实例
def new(cls, *args, **kwargs):
例子
-- coding: utf-8 --
class Foo(object):
def init(self,name):
self.name=name
def __new__(cls, *args, **kwargs):
obj=super(Foo, cls).__new__(cls) #利用object的__new__方法创建新的空对象
obj.__init__(*args,**kwargs)
return obj
f=Foo(‘nike’)
print(f.name)
print(f.dict)
二、__len__方法
如果一个类表现得像一个list,要获取有多少个元素,就得用 len() 函数。
通过len(object)调用
class Student():
def init(self,*args):
self.names = args
def len(self):
return len(self.names)
s = Student(“nick”,“jack”,“pony”)
print(len(s)) #用len直接调用__len__方法
from collections import namedtuple
Card = namedtuple(“Card”,[“rank”,“suit”]) #这里相当于创建了一个名为Card的类,后面的列表里是类的类属性,没有函数属性
#
class FranchDeck:
ranks = [str(n) for n in range(2,11)] + list(‘JQKA’)
suits = [‘红心’,‘方板’,‘梅花’,‘黑桃’]
def __init__(self):
self._cards = [(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] #这里是一个列表生成式,
#将第一个for循环里的值赋值给rank,将第二个for循环里的值赋给suit,这个类只要一实例化就自动生成了一副除了没有大小王的牌
#每张牌都是用元组来表示的
def __len__(self):
return len(self._cards) # choice()方法依赖__len__(self)
def __getitem__(self, item):
return self._cards[item]
def __setitem__(self, key, value):
self._cards[key] = value
deck = FranchDeck() #这里相当于创建了一副牌
print(deck[0]) #打印第一张牌
print(deck[2:5]) #打印第2-4张牌
from random import choice,shuffle
print(choice(deck)) #随机抽取一张牌
shuffle(deck) #洗牌
print(“洗牌后的第一张”,deck[0]) #打印第一张牌
三、__eq__方法
eq(slef,other) ,判断self对象是否等于other对象,使用==或者is调用此方法。
class Foo:
def init(self,name):
self.name = name
def eq(self, other):
if self.name == other.name: #判断如果对象的name属性相等就返回True
return True
else:
return False
obj1 = Foo(“nick”)
obj2 = Foo(“nicholas”)
print(obj1 is obj2)
四、__hash__方法
获取取一个对象的哈希值,一般来说,对于对象的hash值是基于对象的内存地址生成的,但是重写__hash__方法可以自己定制hash取值的对象
class Foo:
def init(self,name,age):
self.name = name
self.age = age
def hash(self):
return hash(self.name+str(self.age)) #这里基于对象的两个属性返回hash值
obj1 = Foo(“nick”,18) #注意hash的对象不能是整数
obj2 = Foo(“nick”,18)
print(hash(obj1))
print(hash(obj2))
注意魔法方法__hash__的使用场景有二:
(1)被内置函数hash()调用
(2)hash类型的集合对自身成员的hash操作:set(), frozenset([iterable]), dict(**kwarg)
五、__eq__和__hash__应用
- 以下代码前半段只用到了__eq__,因为没涉及可hash集合,也就没有涉及到hash
2、如果自定义类重写了__eq__()方法没有重写__hash__()方法,那么这个类无法作为哈希集合的元素使用(这个hashable collections指的是set、frozenset和dict)
比如哈希集合放的全是对象,只定义的__eq__,没定义__hash__,会报错
class Person:
def init(self, name, age):
self.name = name
self.age = age
# def __hash__(self):
# print(self.name, '使用了__hash__方法')
# return hash(self.name)
def __eq__(self, other):
print(self.name, '使用了__eq__方法')
return self.__dict__ == other.__dict__
person1 = Person(‘zs’, 20)
person2 = Person(‘ls’, 20)
person3 = Person(‘ww’, 30)
person4 = Person(‘zs’, 20)
print(person1 == person4)
print(person2 == person3)
set1 = {person1, person2, person3, person4}
print(set1)
输出:
zs 使用了__eq__方法
True
ls 使用了__eq__方法
False
Traceback (most recent call last):
File “D:/self_study/python_study/class_study/python——内置方法.py”, line 88, in
set1 = {person1, person2, person3, person4}
TypeError: unhashable type: ‘Person’
这其实是因为重写__eq__()方法后会默认把__hash__赋为None(文档后面有说),像list一样。如下面测试:
class A:
def eq(self, other):
pass
a = A()
print(a.hash)
hash(a)
输出:
None
TypeError: unhashable type: ‘A’
3. 如果自定义类重写了__hash__()方法没有重写__eq__()方法
class Person:
def init(self, name, age):
self.name = name
self.age = age
def __hash__(self):
print(self.name, '使用了__hash__方法')
return hash(self.name)
# def __eq__(self, other):
# print(self.name, '使用了__eq__方法')
# return self.__dict__ == other.__dict__
person1 = Person(‘zs’, 20)
person2 = Person(‘ls’, 20)
person3 = Person(‘ww’, 30)
person4 = Person(‘zs’, 20)
set1 = {person1, person2, person3, person4}
print(set1)
不报错,但不符合需求———我们认为person1与person4是同一个人,他们却都被添加到集合,违背了集合的理念:
zs 使用了__hash__方法
ls 使用了__hash__方法
ww 使用了__hash__方法
zs 使用了__hash__方法
{<main.Person object at 0x0000000000719CF8>,
<main.Person object at 0x00000000007192E8>,
<main.Person object at 0x0000000000719CC0>,
<main.Person object at 0x0000000000719320>}
我们期望中没有person4.为什么这里会有person4呢?而且person1与person4的hash(name)是相同的,为什么最后还是加到了集合呢? 主要是因为当发现hash出的值相同时,就需要__eq__进行下一步判断。我们重写了__hash__却没重写__eq__,默认调用了object的__eq__,而默认的__eq__比较的是对象地址,person1与person4地址不同,所以将最后的person4加到了集合.
4、自定义类重写了__hash__()方法和__eq__()方法
可哈希的集合(hashed collections),需要集合的元素实现了__eq__和__hash__,而这两个方法可以作一个形象的比喻:
哈希集合就是很多个桶,但每个桶里面只能放一个球。
__hash__函数的作用就是找到桶的位置,到底是几号桶。
__eq__函数的作用就是当桶里面已经有一个球了,但又来了一个球,它声称它也应该装进这个桶里面(__hash__函数给它说了桶的位置),双方僵持不下,那就得用__eq__函数来判断这两个球是不是相等的(equal),如果是判断是相等的,那么后来那个球就不应该放进桶里,哈希集合维持现状。
class Foo:
def init(self, item):
self.item = item
def __eq__(self, other):
print('使用了equal函数的对象的id',id(self))
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __hash__(self):
print('f'+str(self.item)+'使用了hash函数')
return hash(self.item)
f1 = Foo(1)
f2 = Foo(2)
f3 = Foo(3)
fset = set([f1, f2, f3])
print(fset)
print()
f = Foo(3)
fset.add(f)
print(‘f3的id:’,id(f3))
print(‘f的id:’,id(f))
fset.remove(f)
print(fset)
运行结果:
f1使用了hash函数
f2使用了hash函数
f3使用了hash函数
{<main.Foo object at 0x00000203EB7EB630>, <main.Foo object at 0x00000203EB987358>, <main.Foo object at 0x00000203EB987390>}
f3使用了hash函数
使用了equal函数的对象的id 2215860794256
f3的id: 2215860794256
f的id: 2215860797392
使用了equal函数的对象的id 2215860794256
{<main.Foo object at 0x00000203EB7EB630>, <main.Foo object at 0x00000203EB987358>}
可见,在将f1,f2,f3加入到set中时,每次都会调用一次__hash__函数。
由于我定义的___hash__函数是return hash(self.item),所以f和f3找到的桶的位置是同一个位置,因为它俩的item是相同的。当执行fset.add(f)时,f就会调用它自身的__hash__函数,以找到f所属于的桶的位置。但此时桶里已经有别的球了,所以这时候就得用上__eq__来判断两个对象是否相等,从输出可以看出,是已有对象调用__eq__来和后来的对象进行比较(看对象的id)。
这里如果是删除操作fset.remove(Foo(3)),道理也是一样,先用hash找到桶的位置,如果桶里有球,就判断这两个球是否相等,如果相等就把桶里那个球给扔掉。