1,继续上一小节,讲解双下内置方法,双下getitem,setitem,delitem方法,是对象可以像字典一样的来访问属性
# 普通的字典是像下面这样操作的 dic = {'k' : 'v'} dic['k'] = 'v' class Foo: def __init__(self,name,age,sex): self.name = name self.age = age self.sex =sex def __getitem__(self, item): if hasattr(self,item): return self.__dict__[item] def __setitem__(self, key, value): self.__dict__[key] = value def __delitem__(self, key): del self.__dict__[key] def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) # 只要想像字典一样的来操作,就必须实现这几个函数 f = Foo('lisa',100,'女') print(f['name']) f['name'] = 'lucy' f['hobby'] = 'running' print(f['name']) print(f.name,f['name']) del f.hobby # object原生支持 __delattr__ object 原生实现的 # del f['hobby'] # 通过自己来实现的 # print(f.hobby)
2,__new__双下new方法
# __init__ 初始化方法 # __new__ 构造方法:创建一个对象 class A: def __init__(self): self.x = 1 print('in init function') def __new__(cls, *args, **kwargs): # 运行new的时候,理论上来说来还没有self,所以第一个参数是cls, # 但是如果第一个参数修改为self也是可以的,我还不晓得为何 print('in new function') return object.__new__(A,*args,**kwargs) # 我自己定义的new方法是没有办法自己常见类的,需要借助于父类的new方法来创建 a = A() # 运行结果: # in new function # in init function
3,设计模式一共有23种,单例模式,很著名的一种模式,他已经传了好多年了,单例模式很适合在双下New这个地方讲,一个类始终只有一个实例,
a1 = A() a2 = A() a3 = A() print(a1) print(a2) print(a3) # <__main__.A object at 0x10514cf60> # <__main__.A object at 0x105211198> # <__main__.A object at 0x1052111d0> # 如果不是单例模式的话,每个新建的对象都占用新的地址空间
4,单例模式,限制你的类从始至终只能有一个实例,当你第一次实例化这个类的时候,就创建一个实例化的对象,当你之后再来实例化的时候,就用之前创建的对象
class A: __instanse = False # 我不希望别人来用 def __init__(self,name,age): self.name = name self.age = age def __new__(cls, *args, **kwargs): if cls.__instanse: return cls.__instanse cls.__instanse = object.__new__(A) return cls.__instanse egon = A('egg',38) nezha = A('nezha',25) print(egon) print(nezha) # <__main__.A object at 0x10d5b32b0> # <__main__.A object at 0x10d5b32b0> egon.cloth = '小花袄' print(nezha.name) print(egon.name) print(nezha.cloth) # nezha # nezha # 小花袄 # 所有人都在操作一个对象,这就是单例模式,提到单例,一定要提new
5,双下new方法是需要记的,基本面试都会考,只要面试官稍微难缠一点,一定会考的
6,双下eq方法,
class A: def __init__(self,name): self.name = name def __eq__(self, other): # 这个地方可以定制需要比较的内容,这儿不写返回值的话,就不返回的 if self.name ==other.name: return True ob1 = A('egg') ob2 = A('egg') print(ob1 == ob2) # 实现了双下eq就是真,没有实现就是假 print(ob1 is ob2) print(ob1) print(ob2) # <__main__.A object at 0x10eda9198> # <__main__.A object at 0x10eda91d0> # 这儿不一样,所以is是false
7,双下hash 方法
# hash() __hash__ class A: def __init__(self,name): self.name = name a = A('egon') b = A('egon') print(hash(a)) print(hash(b)) # 默认根据对象的内存地址去进行hash # 273542713 # -9223372036581188387
8,定制hash 方法
# 假如希望属性值相同的时候,哈希值也相同,那么就需要定制__hash__函数 class B: def __init__(self,name,gender): self.name = name self.gender = gender def __hash__(self): return hash(self.name+self.gender) a = B('lisa','female') b = B('lisa','female') c = B('mike','male') print(hash(a)) print(hash(b)) print(hash(c)) # -2954662424006399276 # -2954662424006399276 # -5502858135697491787
9,面向对象的进阶,双下内置方法的实例
from collections import namedtuple Card = namedtuple('card',['rank','suit']) # rank牌面的大小,suit是花色,借助于nametuple创建了一个只有属性的类 c1 = Card(2,'红心') # 实例化 print(c1) print(c1.suit)
10,上接第九步,choice 依赖内置的len,shuffle依赖内置的setitem方法
import json from collections import namedtuple Card = namedtuple('Card',['rank','suit']) class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') suits = ['红心','方板','梅花','黑桃'] def __init__(self): self._cards = [Card(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] def __len__(self): return len(self._cards) def __getitem__(self, item): return self._cards[item] def __setitem__(self, key, value): self._cards[key] = value def __str__(self): # 因为返回的必须是字符串,所以需要序列化 return json.dumps(self._cards,ensure_ascii=False) deck = FranchDeck() print(deck[3]) from random import choice print(choice(deck)) # choice依赖内置的len方法 print(choice(deck)) from random import shuffle # shuffle方法依赖内置的setitem方法 shuffle(deck) print(deck[10]) print(deck) print(deck._cards[:5]) # 也实现了切片
11,内置函数 内置模块,内置的基础数据类型 < --- >类的内置方法
== eq
len __len__
12, set函数依赖于hash 和len方法
# 根据名字和性别去重,年龄不在乎 class A: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def __hash__(self): # 只有hash 函数可以用set了,但是结果还是不对 return hash(self.name + self.sex) def __eq__(self, other): if self.name == other.name and self.sex == other.sex: return True else:return False a = A('egg','男', 38) b = A('egg','男', 37) print(set((a,b))) # 依赖对象的hash 和 eq方法,这种情况就是遇到一个解决一个 # {<__main__.A object at 0x10d099588>, <__main__.A object at 0x10d0995c0>} # 很明显没有去重,但是老师当时跑的时候,说报了一个错,就是unhashbale ,所以老是想到这个set 函数是依赖与双下哈市
13,一道面试题
class Person: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __hash__(self): return hash(self.name + self.sex) def __eq__(self, other): if self.name == other.name and self.sex == other.sex:return True p_lst = [] for i in range(84): p_lst.append(Person('lisa',i,'male')) print(p_lst) print(set(p_lst)) # 这个地方他判断相等的时候,不会去考虑中间的年领,所以后面的都会被认为是相等的 # set函数依赖对象的hash eq 方法
14,hashlib,这个模块,只要你是一个程序员,就会一直伴着你,现在学习用,以后登陆用,未来学习socket的时候还会用的,网络编程也会用,揭开hashlib的什么面纱。
# 写登陆认证的时候,会有一个文件保存用户名密码,万一文件丢了,怎么办?比方说你是支付宝 # 加密 肯定对应一个解密功能,但是我们现在这个算法他解不了,只能加密。所以我们给了他一个新名字,叫做摘要算法 # hashlib 能干的事 # 两个字符串,500万个字,即使有一个字符不一样,结果也不一样,计算过程我们先不关系,分别进行摘要,结果就是不一样的 import hashlib # 提供摘要算法的模块 md5 = hashlib.md5() md5.update(b'alex3714') # 必须是bytes类型的 print(md5.hexdigest()) # aee949757a2e698417463d47acac93df,如果我文件里面存的是这一串数字,就不怕丢了 # 摘要算法对同一个字符串进行摘要,结果永远不会变化的,不只是同一次运行,多次结果也是固定的
15,总结:不管算法多么不同,摘要的功能始终不变;对于相同的字符串使用同一个算法进行摘要,得到的值总是不变的;使用不同的算法,对相同的字符串进行摘要,得到的值应该不同;不管使用什么算法,hashlib的方式永远不变。
16,sha 算法
# 调用方法和md5方法是一样的,sha 算法随着算法复杂成都的增加,我摘要的时间成本空间成本都有所增加 sha = hashlib.sha1() sha.update(b'alex3714') print(sha.hexdigest()) sha = hashlib.sha3_224() sha.update(b'alex3714') print(sha.hexdigest()) sha = hashlib.sha3_512() sha.update(b'alex3714') print(sha.hexdigest())
17,今后所有的密码,都应该是密文存储的,不能明文显示,hashlib里面的摘要算法有很多种,md5是最核心,用的最多的。
18,摘要算法的用处:密码的密文存储,文件的一致性检查,很多网站下载的时候也会有那种需要检查,他怕你下载时不完整,在最右边,下载的时候我们会检查我们下载的文件和远程服务器的文件是否一致;两台机器上的两个文件,想检查这两个文件是否相等。比如500台服务器要求资料是一样的,这时候会用到一致性校验
19,