面向对象进阶
一、反射(******)
1 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)

def hasattr(*args, **kwargs): # real signature unknown """ Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. """ pass

def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ pass getattr(object, name, default=None)

def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass setattr(x, y, v)

def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ pass delattr(x, y)

class BlackMedium: feature='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_house(self): print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name) def rent_house(self): print('%s 黑中介租房子啦,傻逼才租呢' %self.name) b1=BlackMedium('万成置地','回龙观天露园') #检测是否含有某属性 print(hasattr(b1,'name')) print(hasattr(b1,'sell_house')) #获取属性 n=getattr(b1,'name') print(n) func=getattr(b1,'rent_house') func() # getattr(b1,'aaaaaaaa') #报错 print(getattr(b1,'aaaaaaaa','不存在啊')) #设置属性 setattr(b1,'sb',True) setattr(b1,'show_name',lambda self:self.name+'sb') print(b1.__dict__) print(b1.show_name(b1)) #删除属性 delattr(b1,'addr') delattr(b1,'show_name') delattr(b1,'show_name111')#不存在,则报错 print(b1.__dict__) 四个方法的使用演示

class Foo(object): staticField = "old boy" def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' @staticmethod def bar(): return 'bar' print getattr(Foo, 'staticField') print getattr(Foo, 'func') print getattr(Foo, 'bar') 类也是对象

#!/usr/bin/env python # -*- coding:utf-8 -*- import sys def s1(): print 's1' def s2(): print 's2' this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2') 反射当前模块成员
导入其他模块,利用反射查找该模块是否存在某个方法

#!/usr/bin/env python # -*- coding:utf-8 -*- def test(): print('from the test')

#!/usr/bin/env python # -*- coding:utf-8 -*- """ 程序目录: module_test.py index.py 当前文件: index.py """ import module_test as obj #obj.test() print(hasattr(obj,'test')) getattr(obj,'test')()
3 为什么用反射之反射的好处
好处一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

class FtpClient: 'ftp客户端,但是还么有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]' %addr) self.addr=addr

#from module import FtpClient f1=FtpClient('192.168.1.1') if hasattr(f1,'get'): func_get=getattr(f1,'get') func_get() else: print('---->不存在此方法') print('处理其他的逻辑') 不影响lili的代码编写
好处二:动态导入模块(基于反射当前模块成员)
#(1) 反射对象 # hasattr getattr setattr delattr class A: def func(self): print('in func') a = A() a.name = 'alex' a.age = 63 # 反射对象的属性 ret = getattr(a,'name') # 通过变量名的字符串形式取到的值 print(ret) #alex # print(a.__dict__) #{'name': 'alex', 'age': 63} var = input('>>>') # age # print(a.__dict__[变量名]) #63 print(getattr(a,var)) #63 # 反射对象的方法 # a.func() #in func ret = getattr(a,'func') ret() #in func #(2) 反射类 class A: price = 20 @classmethod def func(cls): print('in func') # 反射类的属性 # print(A.price) #20 print(getattr(A,'price')) #20 # # 反射类的方法 :classmethod staticmethod print(A.func()) #in func if hasattr(A,'func'): getattr(A,'func')() #in func #(3) 反射模块 import my ''' my.py day = 'Monday' # 周一 def wahaha(): print('wahahaha') class C: pass ''' # 反射模块的属性 # print(my.day) # Monday print(getattr(my,'day')) # Monday # 反射模块的方法 getattr(my,'wahaha')() #wahahaha #(4) 反射内置模块 import time print(getattr(time,'time')()) #1590662183.0512092 print(getattr(time,'asctime')()) #Thu May 28 18:36:23 2020 #(5) 反射自己模块 def qqxing(): print('qqxing') year = 2018 import sys # sys.modules['__main__'] #找到当前模块 print(sys.modules['__main__']) print(sys.modules['__main__'].year) #2018 # 反射自己模块中的变量 print(getattr(sys.modules['__main__'],'year')) #2018# 反射自己模块中的函数 getattr(sys.modules['__main__'],'qqxing')() # #qqxing #main最好写成一个变量name blm = input('>>>') #year print(getattr(sys.modules[__name__],blm)) #2018 # 正常运行的时候name变量等于模块的名字,防止本被其它模块导入造成反射不成功的影响 #(6) 反射模块中的类 import my print(getattr(my,'C')()) #实例化模块my中的类C,得到一个对象 <my.C object at 0x000000000221ADA0> if hasattr(my,'name'): getattr(my,'name') #没有这个变量name,什么都不打印 #(7)settar和delatter 重要程度半颗星 # setattr 设置修改变量 class A: pass a = A() setattr(a,'name','nezha') #这个name存在对象a的内存空间中 setattr(A,'name','alex') #这个name存在类A的内存空间中 print(A.name) #alex print(a.name) #nezha # delattr 删除一个变量 delattr(a,'name') print(a.name) #alex 把对象a的name删除了,会从当前类A中寻找name:'alex' delattr(A,'name') print(a.name) #把类中的name也删除了,报错
反射小示例:
class Teacher: dic = {'查看学生信息':'show_student','查看讲师信息':'show_teacher'} def show_student(self): print('show_student') def show_teacher(self): print('show_teacher') @classmethod def func(cls): print('hahaha') alex = Teacher() for k in Teacher.dic: print(k) key = input('输入需求 :') if hasattr(alex,Teacher.dic[key]): func = getattr(alex,Teacher.dic[key]) func()
反射应用场景:flask配置文件以及Django中间件都用到了反射
二、 isinstance(obj,cls)和issubclass(sub,super)(***)
isinstance(obj,cls):检查是否obj是否是类 cls 的对象
issubclass(sub, super):检查sub类是否是 super 类的派生类
class A:pass class B(A):pass a = A() print(isinstance(a,A)) #True print(issubclass(B,A)) #True print(issubclass(A,B)) #False
三、几个类的内置方法(**)
1、__str__和__repr__
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
# 内置的类方法 和 内置的函数之间有着千丝万缕的联系 class A:pass a=A() print(a) #<__main__.A object at 0x0000000001E1A860> #先去a里面找__str__方法,没有找到的话就去父类object里面找__str__方法 # object 里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址 class Teacher: def __init__(self,name,salary): self.name = name self.salary = salary def __str__(self): #自己实现__str__方法 return "Teacher's object :%s"%self.name def __repr__(self): return str(self.__dict__) def func(self): return 'wahaha' nezha = Teacher('哪吒',250) print(nezha) # 打印一个对象的时候,就是调用nazha.__str__ #Teacher's object :哪吒 print(repr(nezha)) #{'name': '哪吒', 'salary': 250} print('>>> %r'%nezha) #>>> {'name': '哪吒', 'salary': 250} l = [1,2,3,4,5] # 实例化 实例化了一个列表类的对象 print(l) #之所以打印出了[1, 2, 3, 4, 5]是因为重定制了它了str方法 # %s str() 直接打印 实际上都是调用的_str__方法 # %r repr() 实际上都是调用__repr__方法 # repr 是str的备胎:如果当前类中没有str方法但有repr方法,打印str的时候会走当前类的repr方法,但str不能做repr的备胎 # print(obj) / '%s'%obj / str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串 # 如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。 # repr(),只会找__repr__,如果没有找父类的 # 内置的方法有很多 # 不一定全都在object中 class Classes: def __init__(self,name): self.name = name self.student = [] def __len__(self): return len(self.student) def __str__(self): return 'classes' py_s9= Classes('python全栈9期') py_s9.student.append('二哥') py_s9.student.append('泰哥') print(len(py_s9)) #2 print(py_s9) #classes
2、__del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:如果产生的对象仅仅只是python程序级别的(用户级),那么无需定义__del__,如果产生的对象的同时还会向操作系统发起系统调用,即一个对象有用户级与内核级两种资源,比如(打开一个文件,创建一个数据库链接),则必须在清除对象的同时回收系统资源,这就用到了__del__
class A: def __del__(self): # 析构函数: 在删除一个对象之前进行一些收尾工作 print("执行到我啦!") a = A() del a #执行到我啦! del 既执行了这个方法,又删除了变量

class Foo: def __del__(self): print('执行我啦') f1=Foo() # del f1 print('------->') #输出结果 -------> 执行我啦 #为何啊???
3、 __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')() #name alex
4、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('egon',38,'男') print(f['name']) #egon 会触发类中的getitem方法,并把'name'传给itme f['hobby'] = '女' #会触发类中的setitemi方法,并把'hobby'传给key,'女'传给value print(f.__dict__) #{'name': 'egon', 'age': 38, 'sex': '男', 'hobby': '女'} print(f.hobby,f['hobby']) #女 女 # del f.hobby # object 原生支持 __delattr__ del f['hobby'] # 通过自己实现的 会触发类中的delitem方法,并把'hobby'传给key print(f.__dict__) #{'name': 'egon', 'age': 38, 'sex': '男'}
5、__new__(MS)
__init__ 初始化方法
__new__ 构造方法 : 创建一个对象
我们都知道在__init__之前就已经有一个self了,这个self就是__new__造出来的
class A: def __init__(self): self.x = 1 print('in init function') def __new__(cls, *args, **kwargs):#为什么不传self呢,因为new方法之后才有self print('in new function') return object.__new__(A, *args, **kwargs) #创建一个对象self,返回到上面了 a1 = A() ''' in new function in init function ''' a2 = A() ''' in new function in init function ''' print(a1) #<__main__.A object at 0x00000000021BA8D0> print(a2) #<__main__.A object at 0x0000000001E7A8D0>
单例模式:
一个类 始终 只有 一个 实例
当你第一次实例化这个类的时候 就创建一个实例化的对象
当你之后再来实例化的时候 就用之前创建的对象

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 cls.__instance = object.__new__(cls) #如果之前没有创建过对象,创建对象 return cls.__instance egon = A('egg',38) egon.cloth = '小花袄' nezha = A('nazha',25) print(nezha) #<__main__.A object at 0x0000000001EAA9B0> print(egon) #<__main__.A object at 0x0000000001EAA9B0> 和上面内存地址一样,说明是同一个对象 print(nezha.name) #nazha print(egon.name) #nazha print(nezha.cloth) #小花袄 #第一实例化的时候可以比喻女娲造出了一个小人,给小人起了一个名字,赋予了年龄, #第二次实例化的时候拿到的还是之前造出的那个小人,如果小人没有名字和年龄就给它名字和年龄 # 如果有名字年龄就把它们删了重新赋予名字和年龄,也就是它们操作的都是同一个对象
6、__eq__
判断两个对象是否相等,正常情况下比较内存地址,如果重新对它进行定制它对根据定制的条件来判断两个对象是否相等

class A: def __init__(self,name): self.name = name def __eq__(self, other): if self.__dict__ == other.__dict__: return True else: return False ob1 = A('egon') ob2 = A('egg') print(ob1 == ob2) #False 此时==会触发eq方法
7、__hash__
只要是可hash的,内部一定实现了一个__hash__方法,默认根据对象的内存地址进行hash,也可以自定制规则

class A: def __init__(self,name): self.name = name m=A('hui') n=A('hui') print(hash(m)) #2005673 #默认根据对象的内存地址进行hash print(hash(n)) #-9223372036852770132 class B: def __init__(self,name,sex): self.name = name self.sex = sex def __hash__(self): #自定制hash return hash(self.name+self.sex) a = B('egon','男') b = B('egon','男') print(hash(a)) #-4411851472115669131 print(hash(b)) #-4411851472115669131 c = B('egon0','男') d = B('egon0','女') print(hash(c)) #247660479663688395 print(hash(d)) #5874602778707716777
示例:

import json from collections import namedtuple Card = namedtuple('Card',['rank','suit']) # rank 牌面的大小 suit牌面的花色 class FranchDeck: ranks = [str(n) for n in range(2,11)] + list('JQKA') # 2-A suits = ['红心','方板','梅花','黑桃'] def __init__(self): self._cards = [Card(rank,suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits] # for suit in FranchDeck.suits: # for rank in FranchDeck.ranks: # Card(suit,rank) 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[10]) #取牌 #Card(rank='4', suit='梅花') from random import choice print(choice(deck)) #choice依赖内置的len方法 #抽牌 #Card(rank='4', suit='红心') from random import shuffle shuffle(deck) #shuffle依赖内置的setitem #洗牌 print(deck[10]) #Card(rank='8', suit='红心') print(deck) #[["A", "红心"], ["5", "方板"], ["6", "方板"],["2", "方板"], ["3", "方板"],…… ["2", "黑桃"], ["A", "方板"]] print(deck[:5]) #[Card(rank='A', suit='红心'), Card(rank='5', suit='方板'), Card(rank='6', suit='方板'), # Card(rank='2', suit='方板'), Card(rank='3', suit='方板')] # 内置函数 内置的模块 内置的基础类型 < --- >类的内置方法

# 100个人,name/sex/age #每年录入他们的信息,特点 名字 和 性别相同, 年龄不同 #录入的信息怎么和之前录入的信息进行去重 # set class A: def __init__(self,name,sex,age): self.name = name self.sex = sex self.age = age def __eq__(self, other): if self.name == other.name and self.sex == other.sex: return True return False def __hash__(self): return hash(self.name + self.sex) a = A('egg','男',38) b = A('egg','男',37) print(set((a,b))) # unhashable #{<__main__.A object at 0x00000000021DA7F0>} # set 依赖对象的 hash eq