面向对象进阶
一、关于面向对象的两个内置函数
isinstance 判断类与对象的关系 isinstance(obj,cls)检查obj是否是类 cls 的对象,返回值是bool值
issubclass 判断类与类的关系 issubclass(sub, super)检查sub类是否是 super 类的派生类,返回值是bool值
class A: pass class B(A): pass a = A() print(isinstance(a,A)) print(issubclass(B,A)) print(issubclass(A,B)) >>> True True False
二、反射
什么是反射?
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的
提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
ps:
eval 的功能和反射有点类似,但是 eval 在操作字符串时有很大的安全隐患,反射没有安全隐患。
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
1、getattr(object, name[,default])
获取对象object的属性或者方法,如果存在打印出来,如果不存在,打印出默认值,默认值可选。
需要注意的是,如果是返回的对象的方法,返回的是方法的内存地址,如果需要运行这个方法,
可以在后面添加一对括号。
class A: def func(self): print('in func') a = A() a.name = 'alex' a.age = 63 ret = getattr(a,'name') # 通过变量名的字符串形式取到的值 print(ret) print(a.__dict__) 变量名 = input('>>>') # func print(getattr(a,变量名)) print(a.__dict__[变量名]) 》》》 alex {'name': 'alex', 'age': 63} >>>age 63 63
class A: def func(self): print('in func') a = A() a.func() ret = getattr(a,'func') print(ret) #不加括号只能打印一个内存地址 ret() 》》》 in func <bound method A.func of <__main__.A object at 0x00000250C461E668>> in func
class A: price = 20 @classmethod def func(cls): print('in func') print(getattr(A,'price')) 》》》 20
#需要使用到装饰器 classmethod staticmethod class A: price = 20 @classmethod def func(cls): print('in func') if hasattr(A,'func'): getattr(A,'func')() 》》》 in func
#新建一个my.py #内容 day = 'Monday' # 周一 def wahaha(): print('wahahaha') class C: pass #在另一个py文件里运行 import my print(my.day) print(getattr(my,'day')) 》》》 Monday Monday
#新建一个my.py #内容 day = 'Monday' # 周一 def wahaha(): print('wahahaha') class C: pass #在另一个py文件里运行 getattr(my,'wahaha')() 》》》 wahahaha
#新建一个my.py #内容 day = 'Monday' # 周一 def wahaha(): print('wahahaha') class C: pass #在另一个py文件中运行 import my print(getattr(my,'C')()) 》》》 <my.C object at 0x0000029F28E9E080>
# time # asctime import time print(getattr(time,'time')()) print(getattr(time,'asctime')()) 》》》 1516608195.0811734 Mon Jan 22 16:03:15 2018
def qqxing(): print('qqxing') year = 2018 import sys print(sys.modules['__main__'].year) print(getattr(sys.modules['__main__'],'year')) 》》》 2018 2018
def qqxing(): print('qqxing') year = 2018 import sys getattr(sys.modules['__main__'],'qqxing')() 变量名 = input('>>>') print(getattr(sys.modules[__name__],变量名)) 》》》 qqxing >>>year 2018
import time print(time.strftime('%Y-%m-%d %H:%M:S')) print(getattr(time,'strftime')('%Y-%m-%d %H:%M:S')) 》》》 2018-01-22 16:08:S 2018-01-22 16:08:S
2、hasattr(object, name)
判断一个对象里面是否有name属性或者name方法,返回BOOL值,有name特性返回True, 否则返回False。
需要注意的是name要用括号括起来
class A: name = 'xiaoming' def run(self): return 'gogogo' a = A() print(a.run()) print(hasattr(a,'name')) #判断对象是否有 name 属性 print(hasattr(a,'run')) #判断对象是否有 run 方法 print(hasattr(A,'run')) #判断类是否有 run 属性 print(hasattr(A,'rn')) 》》》 gogogo True True True False
3、setattr(object, name, values)
给对象的属性赋值,若属性不存在,先创建再赋值。
class A: pass a = A() setattr(a,'name','nezha') setattr(A,'name','alex') print(A.name) print(a.name)
4、delattr
(object, name)
删除一个变量,删除指定对象的指定名称的属性,和setattr函数作用相反,当属性不存在的时候,会报错,不能删除对象的方法。
class A: pass a = A() setattr(a,'name','nezha') setattr(A,'name','alex') delattr(a,'name') print(a.name) delattr(A,'name') print(a.name) 》》》 alex Traceback (most recent call last): File "D:/Python学习/Python/学习资料/学习视频/day27/2.反射.py", line 122, in <module> print(a.name) AttributeError: 'A' object has no attribute 'name'
在删除时,注意作用域。对象的属性里没有可以去找类的属性,但不能去object这个父类找
三、双下方法
内置的类方法 和 内置的函数之间有着千丝万缕的联系
__str__和__repr__
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
#_*_coding:utf-8_*_ 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_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) ''' str函数或者print函数--->obj.__str__() repr或者交互式解释器--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,否则抛出异常 ''' print(format(s1,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd'))
class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b=B() print('%s'%b) print('%r'%b) %s和%r
# obj.__str__ str(obj) # obj.__repr__ repr(obj) class Teacher: def __init__(self,name,salary): self.name = name self.salary = salary def __str__(self): return "Teacher's object :%s"%self.name def __repr__(self): return str(self.__dict__) def func(self): return 'wahaha' nezha = Teacher('哪吒',250) print(nezha) # 打印一个对象的时候,就是调用a.__str__ print(repr(nezha)) print('>>> %r'%nezha) #a.__str__ --> object # object 里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址 # l = [1,2,3,4,5] # 实例化 实例化了一个列表类的对象 # print(l) # %s str() 直接打印 实际上都是走的__str__ # %r repr() 实际上都是走的__repr__ # repr 是str的备胎,但str不能做repr的备胎 # print(obj)/'%s'%obj/str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串 # 如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。 # repr(),只会找__repr__,如果没有找父类的
ps:
内建函数str()和repr() (representation,表达,表示)或反引号操作符(``)可以方便地以字符串的方式获取对象的内容、类型、数值属性等信息。str()函数得到的字符串可读性好(故被print调用),而repr()函数得到的字符串通常可以用来重新获得该对象,通常情况下 obj==eval(repr(obj)) 这个等式是成立的。这两个函数接受一个对象作为其参数,返回适当的字符串。
事实上repr()和``做一样的事情,返回一个对象的“官方”字符串表示。其结果绝大多数情况下(不是所有)可以通过求值运算(内建函数eval())重新得到该对象。
str()则不同,它生成一个对象的可读性好的字符串表示,结果通常无法用eval()求值,但适合print输出。
__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,
所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): print('执行我啦') f1=Foo() del f1 print('------->') #输出结果 执行我啦 -------> 简单示范
__call__
Python中的__call__允许程序员创建可调用的对象(实例),默认情况下, __call__()方法是没有实现的,这意味着大多数实例是不可调用的。然而,如果在类定义中覆盖了这个方法,那么这个类的实例就成为可调用的。
class A: def __init__(self,name): self.name = name def __call__(self): ''' 打印这个对象中的所有属性 :return: ''' for k in self.__dict__: print(k,self.__dict__[k]) a = A('alex')() #后面的括号可以直接让__call__(self)函数执行 》》》 name alex
__len__
在执行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))
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): 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] f = Foo('wangming',38,'男') print(f['name']) f['hobby'] = '男' print(f.hobby,f['hobby']) del f.hobby # object 原生支持 __delattr__ del f['hobby'] # 通过自己实现的 print(f.__dict__)
__new__
构造方法 : 创建一个对象
class A: def __init__(self): self.x = 1 print('in init function') def __new__(cls, *args, **kwargs): print('in new function') return object.__new__(A, *args, **kwargs) a = A() print(a.x) 》》》 in new function in init function 1
#单例模式 #一个类 始终 只有 一个 实例 #当你第一次实例化这个类的时候 就创建一个实例化的对象 #当你之后再来实例化的时候 就用之前创建的对象 class Singleton: def __new__(cls, *args, **kw): if not hasattr(cls, '_instance'): cls._instance = object.__new__(cls, *args, **kw) return cls._instance one = Singleton() two = Singleton() two.a = 3 print(one.a) # 3 # one和two完全相同,可以用id(), ==, is检测 print(id(one)) # 29097904 print(id(two)) # 29097904 print(one == two) # True print(one is two) 单例模式 单例模式
__eq__
__eq__ 定义了类的等号(==)行为
可以去重
class A: def __init__(self): self.a = 5 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.a) print(a.b) print(b.a) print(b.b) print(a == b) 》》》 5 2 5 2 True
__hash__
摘要算法:密码密文加密
摘要算法只能加密,没有解密功能
# 摘要算法 # import hashlib # 提供摘要算法的模块 # 不管算法多么不同,摘要的功能始终不变 # 对于相同的字符串使用同一个算法进行摘要,得到的值总是不变的 # 使用不同算法对相同的字符串进行摘要,得到的值应该不同 # 不管使用什么算法,hashlib的方式永远不变 # import hashlib # 提供摘要算法的模块 # sha = hashlib.md5() # sha.update(b'这是密码区') # print(sha.hexdigest()) # sha 算法 随着 算法复杂程度的增加 摘要的时间成本空间成本都会增加 # 摘要算法 # 密码的密文存储 # 文件的一致性验证 # 在下载的时候 检查我们下载的文件和远程服务器上的文件是否一致 # 两台机器上的两个文件 你想检查这两个文件是否相等
# 用户注册 # 用户 输入用户名 # 用户输入 密码 # 明文的密码进行摘要 拿到一个密文的密码 # 写入文件 # 用户的登录 import hashlib usr = input('username :') pwd = input('password : ') with open('userinfo') as f: for line in f: user,passwd,role = line.split('|') md5 = hashlib.md5() md5.update(bytes(pwd,encoding='utf-8')) md5_pwd = md5.hexdigest() if usr == user and md5_pwd == passwd: print('登录成功')
# 加盐 提高密码的安全等级 import hashlib # 提供摘要算法的模块 md5 = hashlib.md5(bytes('盐',encoding='utf-8')) #加盐区可以是任意字符 # md5 = hashlib.md5() md5.update(b'123456') print(md5.hexdigest())
# 动态加盐 提高密码的安全等级 # 用户名 密码 # 使用用户名的一部分或者 直接使用整个用户名作为盐 import hashlib # 提供摘要算法的模块 md5 = hashlib.md5(bytes('盐',encoding='utf-8')+b'') # md5 = hashlib.md5() md5.update(b'123456') print(md5.hexdigest())
# 文件的一致性校验 # 文件的一致性校验这里不需要加盐