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')
super()的用法

重点:实现设计模式中的单例模式(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。

posted on 2018-12-04 10:01  smile大豆芽  阅读(142)  评论(0)    收藏  举报