面向对象的进阶(item系列,__new__,__hash__,__eq__)python
1、item系列设计模式
getitem、setitem、delitem(操做过程达到的结果其实就是增删改查)
class Foo: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def __getitem__(self, item): # 与f['xx']形式对应 if hasattr(self, item): return self.__dict__[item] def __setitem__(self, key, value): # 与f['xx']='ss'形式对应 self.__dict__[key] = value def __delitem__(self, key): # 与del f['xx']形式对应 del self.__dict__[key] f = Foo('egon',30,'男') print(f['name']) # f['name']---对象['xx']这种形式就会触发前面的__getitem__方法,将name就传给了item # 支持以这样的方式取得了对象的属性name,正常的是f.name取到属性 f['hobby'] = '男' # 新增长的key和value,触发__setitem__方法,将对应的属性和值放入本来的字典中 print(f['hobby'], f.hobby) # 以f['hobby']这样的方式取到新增的属性,本来正常取值f.hobby # del f.hobby # 正常的删除方式 # print(f.hobby) # 此时会报错,显示:AttributeError: 'Foo' object has no attribute 'hobby' del f['hobby'] # 若是执行到这步,就会显示:AttributeError: __delitem__,显示没有这个方法 # 这个删除方式就触发__delitem__方法,前面类里面就必须得有定义该方法 print(f.__dict__) # 字典里面就没有hobby的属性了
运行结果:spa
C:\Users\3-2\PycharmProjects\untitled\venv\Scripts\python.exe C:/Users/3-2/PycharmProjects/untitled/面向对象进阶.py egon 男 男 {'sex': '男', 'age': 30, 'name': 'egon'} Process finished with exit code 0
这种用中括号就能够直接调的方式,好比用字典,列表的实现过程,就是内部存在了item系列这个机制的缘由
这种用中括号就能够直接调的方式,好比用字典,列表的实现过程,就是内部存在了item系列这个机制的缘由
object原生支持__delattr__因此才能够直接del f.hobby而不报错,可是del f['hobby']得经过本身实现,
因此当类方法里面没有__delitem__的时候就会报错
2、__new__
__init__:初始化方法
__new__:构造方法,建立一个对象。self就是__new__构造出来的,即__new__方法是self产生的机制
平时是不须要用到执行__new__方法的,如下例子只是简单说明它是怎么用的:
class A: def __init__(self): self.x = 1 print('in init function') def __new__(cls, *args, **kwargs): # 传入一个默认参数cls,执行__new__方法前尚未self,因此只能传一个类进来 print('in new function') return object.__new__(A,*args,**kwargs) # object.__new__创造了一个新的对象,而后将这个对象传给self的位置,因此当执行self的时候就可使用对象了 a = A() # 实例化,会先执行__new__方法,再执行 __init__方法
运行结果:设计
in new function in init function Process finished with exit code 0
一个典型的设计模式(23种):单例模式
单例模式:一个类始终只有一个实例;当第一次实例化这个类的时候就建立一个实例化的对象;当以后再来实例化的时候, 就会用以前建立的对象
# 实现单例模式的例子:
class A: __instance = False # 私有的静态变量 不但愿别人可使用,必须得通过本身的设置 def __init__(self,name,age): self.name = name self.age = age def __new__(cls, *args, **kwargs): if cls.__instance: # 若是为真就执行下面代码,不然执行后面的代码 # 第二次进来的时候就符合这个了 return cls.__instance # 第二次进来就直接将以前建立的对象返回给self了 cls.__instance = object.__new__(A) # 由于第一次进来的时候就是__instance = False,因此执行这行代码代码 # 用object.__new__建立一个类A的新的对象,而且赋值给 cls.__instance return cls.__instance # 这里就是将新建的对象return回去 egon = A('egg',38) # 真正能实例化对象而且占用内存的是object里面的self,可是这里使用的对象始终是object.__new__建立的, # 由于本身就有对象了,就不会去使用object里面的了 # 反正可以实现单例化的缘由是__new__方法的使用 egon.cloth = '小花袄' nezha = A('nazha',25) nezha.shoes = '小白鞋' print(nezha) print(egon) # 执行到这里根据运行结果显示内存地址是同一个,也就是说第二次实例化的时候是在对第一个实例化后的 # 对象进行操做的,而并无再次建立另外一个占内存的对象,若是第二次实例化传的参数和原对象一致, # 参数值就会进行覆盖,若是第二次实例化传的参数只是原属性的一部分,则相同的覆盖,原来的继续会 # 在表如今现有对象中 print(nezha.name) print(egon.name) # 执行到这里原来egon的名字已经被nezha覆盖了 print(nezha.cloth) # 执行到这里原来egon的cloth会继续穿在nezha上
运行结果:code
<__main__.A object at 0x0000025C0C2293C8> <__main__.A object at 0x0000025C0C2293C8> nazha nazha 小花袄
3、__hash__对象
# 在没有定义__hash__方法的时候,hash都是针对内存地址的,而不是针对对象属性,内存地址不同,hash的结果也不同
class A:
def __init__(self,name,sex):
self.name = name
a = A('egn','男')
b = A('egon','nv')
print(hash(a))
print(hash(b))
运行结果:blog
154259419512 154259419617
# 定义了—__hash__方法后,属性不一样hash值也会不一样,属性相同hash值也会相同: class A: def __init__(self,name,sex): self.name = name self.sex = sex def __hash__(self): return hash(self.name+self.sex) a = A('egon','男') b = A('egon','男') c = A('egon','nv') print(hash(a)) print(hash(b)) print(hash(c))
运行结果:ip
8385798543724353936 8385798543724353936 -7270162062837990016
4、__eq__内存
没有__eq__方法的时候,二者比较是比较内存地址: class A: def __init__(self,name): self.name = name obj1 = A('egg') obj2 = A('egg') print(obj1 == obj2) # 没有定义__eq__方法的时候,比较时候默认比较内存地址,上面两个内存地址是不同的
运行结果:get
False
# 定义__eq__方法时能够本身设定执行内容,‘==’触发的_eq_方法
class A:
def __init__(self,name):
self.name = name
def __eq__(self, other):
if self.name == other.name:
return True
else:
return False
obj1 = A('egg')
obj2 = A('egg')
obj3 = A('EGG')
print(obj1 == obj2) # 等号触发的__eq__
print(obj2 == obj3) # 等号触发的__eq__
运行结果:
True
False
Process finished with exit code 0