Day28 of learning python -面向对象的内置函数,item系列与hashlib
1.__ str__和__repr__
改变对象的字符串显示__ str__和__repr__
自定制格式化字符串__format__
str函数或者print函数-->obj.__str__(),repr函数或者交互式解释器-->obj.__repr__(),如果__str__没有被定义,那么就会使用__repr__来代替
注意:这两种方法的返回值必须式字符串,否则会抛出异常
一个星(*):表示接收的参数作为元祖来处理,两个星(**):表示接收的参数作为字典来处理,在函数作为参数传入时,是聚合的作用。
format格式化函数:通过{}和:来代替以前的%,比如print("网站名:{name}, 地址 {url}".format(name="百度", url="www.baidu.com"))
format_dict={ 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名 } class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=type def __repr__(self): return 'School(%s,%s)' %(self.name,self.addr) def __str__(self): return '(%s,%s)' %(self.name,self.addr) def __format__(self, format_spec): # if format_spec if not format_spec or format_spec not in format_dict: # format_spce 为空或者,在不在format_spec内 format_spec='nat' fmt=format_dict[format_spec] return fmt.format(obj = self) s1=School('oldboy1','北京','私立') print('from repr: ',repr(s1)) print('from str: ',str(s1)) print(s1) print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd')) 结果: dict_items([('apple', 'fruit'), ('cabbage', 'vegetable')]) apple fruit apple = fruit cabbage vegetable cabbage = vegetable
同样:%s和%r也可以调用__str__,和__repr__
class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b = B() print('%s' % b) print('%r' % b) 结果: str : class B repr : class B
2.__del__
析构方法,当对象在内存中被释放时,自动触发执行,此工作都是交给Python解释器来执行的,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的
class Foo: def __del__(self): print('执行我啦') f1=Foo() del f1 print('------->') #输出结果 执行我啦 ------->
3.item系列
__getitem__\__setitem__\__delitem__
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) f1=Foo('sb') print(f1.__dict__) f1['age']=18 print(f1.__dict__) f1['age1']=19 print(f1.__dict__) del f1.age1 # 调用的是__delattr__ print(f1.__dict__) del f1['age'] # 调用的是__delitem__ print(f1.__dict__) f1['name']='alex' print(f1.__dict__) 结果是: {'name': 'sb'} {'name': 'sb', 'age': 18} {'name': 'sb', 'age': 18, 'age1': 19} del obj.key时,我执行 {'name': 'sb', 'age': 18} del obj[key]时,我执行 {'name': 'sb'} {'name': 'alex'}
4.__new__
作用是:主要是当你继承一些不可变的class时(比如int,str,tuple),提供给你一个自定义这些类的实例化过程的途径。
假如我们需要一个永远都是正数的整数类型,通过集成int,我们可能写出这样的代码
class PositiveInteger(int): def __new__(cls, value): return super(PositiveInteger, cls).__new__(cls, abs(value)) # self表示一个具体的实例本省,cls表示这个了类的本身。类先调用__new__方法,返回该类的实例对象,这个实例对象就是__init__方法的第一参数self,即self是__new__的返回值 i = PositiveInteger(-3) print(i)
结果:
3
super()函数是用于调用父类的一个方法,是用来解决多重继承问题,会涉及到查找顺序(MRO)、钻石继承等,格式是:
super().xxx或者super(class,self).xxx,super(type[,object or type])
class FooParent(object): def __init__(self): self.parent = 'I\'m the parent.' print ('Parent') def bar(self,message): print ("%s from Parent" % message) class FooChild(FooParent): def __init__(self): # super(FooChild,self) 首先找到 FooChild 的父类(就是类 FooParent),然后把类B的对象 FooChild 转换为类 FooParent 的对象 super(FooChild,self).__init__() print ('Child') def bar(self,message): super(FooChild, self).bar(message) print ('Child bar fuction') print (self.parent) if __name__ == '__main__': fooChild = FooChild() fooChild.bar('HelloWorld')
重点:实现设计模式中的单例模式(singleton),因为类每一次实例化后产生的过程都是通过__new__来控制的,所以通过重载__new__方式,可以很简单的的实现单例模式。
class Singleton(object): def __new__(cls): # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象 if not hasattr(cls, '_instance'): cls._instance = super(Singleton, cls).__new__(cls) # cls类的本身的意思,cls表示这个类的本身,类先调用__new__方法,返回该类的实例对象,这个实例对象就是__init__方法的第一个参数self,即self是__new__的返回值 return cls._instance obj1 = Singleton() obj2 = Singleton() obj1.attr1 = 'value1' print(obj1.attr1,obj2.attr1) # value,value print(obj1 is obj2) # True
5.__call__
对象后面加括号,触发执行
注意:构造方法的执行是由创建对象触发的,即:对象=类名();而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__
6.__len__
重定制__len__方法
class A: def __init__(self): self.a = 1 self.b = 2 def __len__(self): return len(self.__dict__) a = A() print(len(a))
结果:2
7.__hash__
定制hahs值,对数字的hash值是原来的数值
class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a)+str(self.b)) a = A() print(hash(a)) 结果: 4487187773071864930
8.__eq__
==,定制其相等的条件
class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self,obj): if self.a == obj.a and self.b == obj.b: return True a = A() b = A() print(a == b)
9.来用上述的内置方法来玩一个纸牌游戏
10.hashlib模块
hashlib提供了常见的摘要算法,如MD5,SHA1,摘要算法是通过一个函数,把任意长度的数据转换成一个长度固定的数据串(通常用16进制的字符串表示),摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。
计算一个字符串的MD5值
import hashlib md5 = hashlib.md5() md5.update('how to use in python hashlib?'.encode('utf-8')) print(md5.hexdigest())
MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:
import hashlib sha1 = hashlib.sha1() sha1.update('how to use sha1 in '.encode('utf-8')) sha1.update('python hashlib?'.encode('utf-8')) print(sha1.hexdigest())
SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长。
一般我们存储的话,要存储的是密文,不能是明文的方式,否则会泄露的
如果用户的密码设置的简单,以免被别人用MD5的反推表,那么就通过对程序进行“加盐”操作的。
hashlib.md5("salt".encode("utf-8"))
经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。
但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?
如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。
浙公网安备 33010602011771号