一、集合(set)元素去重的判断依据是什么
1、计算的哈希值一致(前提)
2、内容一致;
在满足计算的哈希值一致的前提下,内容一致则会进行去重;
a = (1,2,3)
b = (1,2,3)
s1 = {a,b}
print(hash(a),hash(b)) #---->2528502973977326415 2528502973977326415
print(a == b) #---->True
print(a is b) #---->False
print(id(a),id(b)) #---->2200332504520 2200332504664
print(s1) #---->{(1, 2, 3)}
#1、比较哈希值,a,b的哈希值一样;(满足前提)
#2、比较内容a == b,返回True,内容一致;(满足第二个条件)
#3、a is b,比较的是内存地址;
# (内存中由两个对象,标识符是a,b,他们的内存地址不一致)
#4、这个比较同时满足了两个条件:哈希值一致、内容一致,在集合中去重;
class Person:
def __init__(self,name):
self.name = name
def __hash__(self):
return 100
def __eq__(self, other):
return self.name == other.name
def __repr__(self):
return "<{} {}>".format(self.__class__,self.name)
__str__ = __repr__
t1 = Person('zzh')
t2 = Person('zhy')
print(hash(t1),hash(t2)) #---->100 100
print(t1 == t2) #---->使用的是__eq__方法,比较的是self.name,一致
print(t1 is t2) #---->比较的是内存地址,这是两个不同的实例,内存地址不一致
print(id(t1),id(t2)) #---->两个实例的内存地址
print({t1,t2}) #---->{<<class '__main__.Person'> zhy>, <<class '__main__.Person'> zzh>}不进行去重
二、总结本周视频讲解魔术方法的作用及什么情况会执行到该方法;
实例构造:
__new__
实例话的两个阶段:
1、__new__构造实例;(需要构造完成实例以后才能初始化)
2、__init__初始化(属性赋值);
class A:
def __new__(cls, *args, **kwargs):
#return super(A,cls).__new__(cls)
return object.__new__(cls)
def __init__(self,x,y):
self.x = x
self.y = y
def show(self):
return self.x,self.y
t = A(4,5)
print(t.show())
#1、通过__new__方法构造了一个实例;
#将当前构造方法交给父类进行构造,返回一个实例;(object中__new__是staticmethod)
#2、通过__init__方法为实例属性赋值;
可视化:
用于对实例的打印输出:
__str__
__repr__
class Person:
def __init__(self,name):
self.name = name
def __showname(self):
return self.name
def __str__(self):
return self.__showname()
def __repr__(self):
return self.__showname()
t = Person('tom')
print(t)
print([t])
#1、__str__直接作用会调用该方法,比如print(t)、str(t)、"{}".format(t)这种情况下;
#2、__repr__间接调用会调用该方法,比如print({t}),print([t]),套到一个容器里头(tuple例外);
#3、如果没有__str__,直接、间接调用都用__repr__
#4、如果没有__repr__,间接调用会调到object中定义__repr__方法,返回的是object定义的格式;
bool的计算与len
__bool__
__len__
class PersonalInformation:
def __init__(self):
self.lst = []
def setinformation(self,*args):
for i in args:
self.lst.append(i)
def __repr__(self):
return "<personal information: {}>".format(self.lst)
__str__ = __repr__
def __bool__(self):
return True
def __len__(self):
return len(self.lst)
t = PersonalInformation()
print(t)
if bool(t):
t.setinformation('zhangzhonghao',27,'manle')
print(t)
#1、将PersonalInformation类实例化,创建一个空列表;
#2、打印空列表
#3、当bool(t)返回为True,则调实例setinformation方法,为列表注入参数;
#4、打印注入参数以后的列表;
#5、如果说没有__bool__方法,则会bool函数中注入len(t)--->bool(len(t));
运算符重载
__eq__
__gt__
__ge__
__lt__
__le__
__add__
__iadd__
__sub__
__isub__
class Person:
def __init__(self,name,age,height):
self.name = name
self.age = age
self.height = height
def __eq__(self, other):
return self.age == other.age
def __gt__(self, other):
return self.height > other.height
def __ge__(self, other):
return self.height >= other.height
def __repr__(self):
return "<Person name:{} age:{} height:{}>".format(self.name,self.age,self.height)
__str__ = __repr__
def __add__(self, other):
return self.age + other.age
def __sub__(self, other):
return self.age - other.age
def __iadd__(self, other):
self.age += other.age
return __class__(self.name,self.age,self.height)
def __isub__(self, other):
self.age -= other.age
#实例化,注入属性
zzh = Person('zhangzhonghao',27,171)
km = Person('km',24,160)
#调__repr__打印属性内容
print(zzh)
print(km)
#比较属性
print(zzh > km)
print(zzh < km)
#属性重新赋值,return self 则就地修改当前实例
#return __class__(self.name,self.age,self.height)则构造新实例
zzh += km
print(zzh)
容器化
__missing__,用于字典,在一个字典的子类实例中,用key去查找value,keyError时会返回__missing__方法中return的值;
class Dic(dict):
def __init__(self):
super(Dic, self).__init__(self)
def additems(self,**kwargs):
self.update(kwargs)
def __missing__(self, key):
return '666'
t = Dic()
t.additems(a =1 ,b =2 ,c =3)
print(t['d'])
print(t)
容器化实践:
需求:构建一个列表实例,能够实现:
1、实例化; t = ShoppingCar()
2、该实例能够添加、删除元素;t.additems(‘a’)
3、能够使用’+’,’-'来添加删除元素;t.subaddtems(‘b’)
4、在删除元素时如果超出索引则提示index out of range,什么都不做,只是提示,记录日志;
5、能够使用index查找元素,索引则提示index out of range,什么都不做,只是提示,记录日志;
6、能够使用index去修改元素内容,正索引超界则append,负索引超界则insert,在索引范围内则正常查询;t[-100] = 100, t[200] = 200,t[1] = 666
7、能够打印实例、能够迭代实例、能够查看实例长度;
import logging
ValueErr = logging.getLogger('ValueErr')
IndexErr = logging.getLogger('IndexErr')
ValueErr.setLevel(20)
IndexErr.setLevel(20)
h1 = logging.StreamHandler()
h2 = logging.FileHandler('F:/t1.log')
ValueErr.addHandler(h1)
ValueErr.addHandler(h2)
IndexErr.addHandler(h1)
IndexErr.addHandler(h2)
f1 = logging.Formatter('%(levelname)s %(asctime)s %(name)s'
'<%(message)s>\t[error file num:%(lineno)s]'
'\t[%(pathname)s]')
h1.setFormatter(f1)
h2.setFormatter(f1)
class ShoopingCar:
def __init__(self):
self.lst = []
def additems(self,item):
return self.__add__(item)
def subitems(self,item):
return self.__sub__(item)
def __add__(self, item):
self.lst.append(item)
return self
def __sub__(self, item):
try:
if item in self.lst:
self.lst.remove(item)
return self
else:
raise ValueError()
except ValueError:
ValueErr.info("do noting %%item is not in {}".format(self.__class__.__name__))
def __getitem__(self, index):
try:
if index <= len(self.lst) -1 and index >= -len(self.lst) +1:
return self.lst[index]
else:
raise IndexError()
except IndexError:
IndexErr.info("do noting %%Index is out of range")
def __setitem__(self, index, value):
if index > len(self.lst) - 1:
self.lst.append(value)
elif index < -len(self.lst) + 1:
self.lst.insert(0,value)
else:
self.lst[index] = value
def __len__(self):
return len(self.lst)
def __iter__(self):
return iter(self.lst)
def __repr__(self):
return "{}".format(self.lst)
def __contains__(self, item):
if item in self.lst:
return True
else:
return False
__str__ = __repr__
t = ShoopingCar()
t + 'a' + 'b' + 'c' + 'd'
print(t)
t[100]
t - 'z'
t[-100] = 100
t[200] = 200
print(t)
可调用对象
定义一个类,将该类实例化,这个实例能够像函数一样调用;
__call__
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def add(self):
return self.x + self.y
def __call__(self, *args, **kwargs):
return self.add()
t = Point(4,5)
print(t())
上下文管理
如果有上下文管理,则函数的执行流程为:
__init__ -----> __enter__ ----> with里面的语句块 ----> __exit__
上下文管理类似于try ..... finally语句,__exit__中的内容不管有没有出现异常,一定会执行;
__enter__:调用之前增强
__exit__:调用之后增强
类似于装饰器,函数执行之前需要干什么,执行完以后需要干什么;
import datetime
import time
class A:
def __init__(self):
pass
def __enter__(self):
self.start = datetime.datetime.now()
def __exit__(self, exc_type, exc_val, exc_tb):
delta = datetime.datetime.now() - self.start
print(delta)
def add(x, y):
time.sleep(2)
print(x + y)
t = A()
with t as t1: # 等价于 f = t__enter__()
add(1, 3)
反射
在程序被加载到内存中执行以后,能够反射出类型的信息;
内建函数:
getattr:用变量名称去找实例属性,写入的name必须为str;----> 等价于t.a
setattr:用变量名称去动态添加实例属性;---->等价于t.x = 100
hasattr:使用变量名称去查看实例属性是否存在,返回bool;---->等价于
class A:
def __init__(self,a,b,c):
self.a = a
self.b = b
self.c = c
t = A(4,5,6)
print(t.a) #---->等价于 getattr(t,'a')
setattr(t,'a',100) #---->等价于 t.a = 100
print(getattr(t,'a'))
print(hasattr(t,'z'))
魔术方法:
__getattr__:如果提供了该方法,在AttributeError时,返回的值为该方法中return的值;
__setattr__:拦截实例属性赋值,修改等,;instance.x = 100或者setattr(instance,'x',100)这种方式时,会调用到__setattr__;
__delattr__:删除实例属性;del instance.x这种方式会调用到__delattr__;需要直接操作字典,不能使用delattr(self,item)会出现递归;
__getattribute__:属性访问第一站,拦截了实例调属性;隐藏实例的__dict__
class Point:
def __init__(self,x,y):
self.x = x
self.y = y
def __getattr__(self, item):
return 10010
def __setattr__(self, key, value): #拦截属性的赋值、修改
print('我拦截了__init__属性赋值干的活')
#super(Point, self).__setattr__(self)
self.__dict__[key] = value
def __delattr__(self, item):
super(Point, self).__delattr__(item)
#self.__dict__.pop(item)
def __repr__(self):
return "<{} {} {}>".format(self.__class__.__name__,self.x,self.y)
t = Point(4,5) #实例化传入参数 4,5
print(t.z) #打印实例属性z,抛出AttributeError,调__getattr__方法;
t.x = 'A' #实例属性重新赋值
del t.x #删除实例属性,调__delattr__方法;
print(t) #打印实例:<Point 10010 5>,由于x属性被删除,打印时报AttributeError,调__getattr__方法,返回10010;
__getattribute__
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __getattribute__(self, item): # 拦截了实例调属性;隐藏实例的__dict__;
print('我是第一站,我拦截了你调用属性,你调用的属性都是我的return值')
return 666
# return super().__getattribute__(item)
t = Point(100, 200)
print(t.x) ---->先打印内容,在返回666
print(t.__dict__) ---->打印内容,实例字典内容为666
当实例去调用属性时查找顺序为:
1、__getattribute__()里面return 的值;
MRO
2、实例字典
3、类字典
4、父类字典
5、__getattr__()定义的return 的值;
三、结合本周所学内容,实现如下类:
class Ob:
pass
执行以下操作:
a = Ob('tom')
b = Ob('tom')
print('a: ', a)
a[0] = 'a'
print('a:', a)
a * 3
print('len: ', len(a))
del a[1]
print('a: ', a)
print('set: ', {a, b})
其输出如下:
a: ['1', '2']
a: ['a', '2']
len: 6
a: ['a', 'a', '2', 'a', '2']
set: {<Ob name=tom items=['a', 'a', '2', 'a', '2']>}
class Ob:
def __init__(self,name):
self.name = name
self.lst = [1,2]
def __str__(self):
return "{}".format(self.lst)
def __repr__(self):
return "<object name={},items={}>".format(self.name,self.lst)
def __setitem__(self, key, value):
self.lst[key] = value
def __mul__(self, nums):
self.lst *= nums
def __len__(self):
return len(self.lst)
def __delattr__(self, item):
self.lst.remove(item)
def __delitem__(self, value):
self.lst.pop(value)
def __hash__(self):
return 100
def __eq__(self, other):
return self.name == other.name
a = Ob('tom')
b = Ob('tom')
print('a: ', a)
a[0] = 'a'
print('a:', a)
a * 3
print('len: ', len(a))
del a[1]
print('a: ', a)
print('set: ', {a, b})
输出: