2020-12-14

一、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__应用

  1. 以下代码前半段只用到了__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找到桶的位置,如果桶里有球,就判断这两个球是否相等,如果相等就把桶里那个球给扔掉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值