python之路27 -- 之面向对象进阶(反射)与双下方法
isinstance和issubclass
class A: pass class B(A): pass a = A() print(isinstance(a,A)) #判断对象a是不是A类的对象,是则返回True,否则返回False print(issubclass(B,A)) #判断B类是不是A类的子类,是则返回True,否则返回False
反射
1、什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2、python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
- 下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
- python中的反射:是用字符串类型的名字,去操作变量
- 反射的应用场景:知道有这个变量,但是我只能拿到和变量名一样的字符串,这种情况下就可以用反射的方法,反射对象中的属性及方法
2.1、反射对象的方法和属性
class A: def func(self): print('in func') a = A() #实例化a对象 a.name = 'ouyang' #给a对象添加name属性 if hasattr(a,'func'): #判断a对象下是否存在func这个变量,存在则返回True,不存在则返回False pub_func = getattr(a,'func') #需要传对象,想要获取的变量 pub_func() #反射对象的方法,结果为:in func print(getattr(a,'name')) #反射对象的属性,结果为:ouyang
2.2、反射类的属性
class A: price = 20 #类的静态属性 def func(self): print('in func') print(A.price) #正常获取A类的price属性对应的值 if hasattr(A,'price'): #判断A类是否存在price的属性 print(getattr(A,'price')) #用反射获取A类的price属性对应的值
2.3、反射类的方法
class A: price = 20 #静态属性 @classmethod #需要加类方法装饰器 def func(self): #类方法 print('in func') if hasattr(A,'func'): #判断,如果存在A类及A类下func方法,如果存在则返回True,不存在则返回False getattr(A,'func')() #反射A类下的func方法
2.4、反射模块下的方法
2.4.1、自定义一个模块my
day = 'monday' #周一 def wahaha(): print('my , wahaha')
2.4.2、反射模块的属性
import my print(my.day) #正常获取my模块的day属性 if hasattr(my,'day'): #判断my模块是否有day属性 print(getattr(my,'day')) #反射获取获取my模块下的day属性
2.4.3、反射模块的方法
import my my.wahaha() #正常获取my模块下的wahaha方法 if hasattr(my,'wahaha'): #判断my模块下是否存在wahaha方法 getattr(my,'wahaha')() #反射获取my模块下的wahaha方法
2.5、反射内置模块下的方法
import time #导入time模块 if hasattr(time,'time'): #判断 print(getattr(time,'time')()) #反射
2.6、反射自己函数的变量
def uxuan(): print('优选书城') yisou = "易搜" import sys if hasattr(sys.modules[__name__],'yisou'): #判断 print(getattr(sys.modules[__name__],'yisou')) #反射当前模块下的变量名 getattr(sys.modules[__name__],'uxuan')() #反射当前脚本下的函数名
2.7、设置一个变量(用的不多)
class A: pass a = A() #实例化一个对象a setattr(a,'name','vip') #给a对象添加一个name属性 setattr(A,'name','net') #给A类添加了一个静态属性name print(A.name) #获取A类的name静态属性,结果为:net print(a.name) #获取a对象的name属性,结果为:vip
2.8、删除一个变量(用的不多)
class A: pass a = A() setattr(a,'name','vip') #给a对象添加一个name属性 setattr(A,'name','net') #给A类添加了一个静态属性name print(A.name) print(a.name) #删除一个变量 delattr(a,'name') #删除对象的name属性 print(a.name) #对象里没有了name属性,对象去类里获取了name属性 delattr(A.name) #删除类的name属性 print(a.name) #没有了,在获取就报错了,TypeError: delattr expected 2 arguments, got 1
四个自省函数说明
hasattr:判断这个名字是否可以反射,可以反射则返回True,不可以反射则返回False
getattr:使用字符串类型的名字可以获取到变量的值
setattr:设置一个变量
delattr:删除一个变量
双下方法
1、__str__和__repr__方法
说明:使用str(a)的时候实际是调用的a对象里的.__str__方法,如果a对象里没有.__str__方法,就会到object里去找__str__方法,因为在python中所有类都是继承object类的
1.1、__str__方法
#1、了解__str__方法 class A: def __str__(self): return "A's object" a=A() #实例化一个对象a print(a) #打印一个对象的时候,就是调用:对象.__str__方法,如果对象里没有.__str__方法就默认继承父类,然后到object下的__str__方法,结果为:A's object #a.__str__ --> object ,object里有一个__str__,一旦被调用,就会返回调用这个方法的对象的内存地址 print('%s:%s'%('A',a)) #%s 、str() 、直接打印实际上都是调用的.__str__方法,结果为:A:A's object #2、__str__的正确打开方式 class Teacher: def __init__(self,name,salary): #初始化方法 self.name = name self.salary = salary def __str__(self): return "Teacher's object :%s"%self.name ouyang = Teacher('欧阳',25000) print(ouyang) #打印一个对象的时候,就是调用:对象.__str__方法,结果为:Teacher's object :欧阳
1.2、__repr__方法
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): #__repr__方法 return str(self.__dict__) ouyang = Teacher('欧阳',25000) #实例化ouyang这个对象 print(ouyang) #打印一个对象的时候,就是调用:对象.__str__方法,如果对象里没有.__str__方法,就默认去找父类的str方法,结果为:Teacher's object :欧阳 print(repr(ouyang)) #%r 、repr()实际上都是走的:对象.__repr__方法,结果为:{'name': '欧阳', 'salary': 25000} print('...%r'%ouyang) #结果为:...{'name': '欧阳', 'salary': 25000}
说明:
- repr是str的备胎,即:有__str__则调用__str__的方法,没有__str__则调用__repr__的方法,__str__和__repr__方法都没有才会找父类的的str方法和repr方法
- 反之:调用__repr__方法的时候,没有__repe__时不会调用__str__的方法
- print(obj),'%s',str(obj)的时候:实际上是调用了obj.__str__方法,如果__str__方法有,那么他返回的必须是一个字符串(如果返回其他类型的数据就会报错)
- 如果没有__str__方法,会先找本类中的__repr__方法,在没有再到父类中找__str__方法、然后到父类中找__repr__,直到object类里也没有就会报错了(object:内置__str__和__repr__)!!
- repr():只会找对象.__repr__,如果没有就找父类,父类没有就找object类,object类里也没有就会报错
- 如果再一个类里面只能实现__repr__或__str__一个方法,怎么实现:一定要实现__repr__,因为__repr__方法实现了之后可以给__str__用
2、__len__方法
前面说到__str__和__repr__方法在object类中存在,如果当前调用对象里没有就会去object类中去继承,这里的__len__方法默认object中没有!!
2.1、了解__len__方法
class A: def __len__(self): return 10 a = A() print(len(a)) #结果为:10 class A: pass a = A() print(len(a)) #这里会报错,TypeError: object of type 'A' has no len(),说没有len方法
2.2、__len__方法的正确打开方式
class Classes: #定义classes类 def __init__(self,name): #类的初始化方法 self.name = name self.student = [] def __len__(self): #定义__len__方法 return len(self.student) py = Classes('python') #实例化一个对象 py.student.append('李四') #往py对象的student属性里追加内容 py.student.append('张三') py.student.append('王五') print(len(py)) #获取py对象的长度,结果为:3
3、析构方法,__len__方法
析构方法:即 再删除一个变量之前在做一些收尾的工作
class A: #定义类 def __del__(self): #__del__方法 print("执行我啦") a = A() #实例化a对象 del a #del删除a对象,即执行了这个方法,又删除了变量(先执行方法,然后在进行删除操作(再删除一个变量之前在做一些收尾的工作)) print(a) #删除变量后,再打印就报错了,NameError: name 'a' is not defined,说没有name a
说明:引用计数:即变量的引用次数为0时,这个变量会被python解释器删除。变量设置的次数超过python解释器的设置值时会被python解释器删除(了解即可),只要删除就一定会执行del函数
4、__call__方法
class A: #定义A类 def __init__(self,name): #初始化方法 self.name = name def __call__(self): #__call__方法 print("执行我啦") a = A('ouyang') #实例化一个a对象 a() #对象():对象名+()相当执行call方法,结果为:执行我啦 A('ouyang')() #相当于:对象(),结果为:执行我啦
5、item系列
- __getattr__
- __setattr__
- __delattr__
class Foo: def __init__(self,name,age,sex): #初始化方法 self.name = name self.age = age self.sex = sex def __getitem__(self, item): #getattr方法 if hasattr(self,item): return self.__dict__[item] def __setitem__(self, key, value): #setattr方法 # print(key,value) self.__dict__[key] = value def __delitem__(self, key): #delattr方法 del self.__dict__[key] f = Foo('ouyang',22,'男') #实例化一个对象 print(f['age']) #对象['']:方法会自动触发类里的__getitem__(self,item)方法,结果为:22 f['hobby'] = 'shou' #对象['k'] = v:方法会自动触发类里的__setitem__(self, key, value)方法,添加属性 print(f.hobby,f['hobby']) #对象的正常调用hobby属性和通过__getitem__(self,item)的方式调用属性,结果为:shou shou # del f.hobby #正常删除对象下属性的方法,del 对象.属性,会触发对象里的__delattr__方法,默认object类中有__delattr__方法 # print(f.__dict__) #结果为:{'name': 'ouyang', 'age': 22, 'sex': '男'},发现hobby属性被删除了 del f['hobby'] #使用del 对象['属性']:方法时,会触发类里的__delitem__(self, key)方法 print(f.__dict__) ##结果为:{'name': 'ouyang', 'age': 22, 'sex': '男'},发现hobby属性被删除了
6、__new__构造方法
class A: def __init__(self): #3、然后执行__init__方法 self.x = 1 print('in init function') def __new__(cls, *args, **kwargs): #2、实例化对象的时候首先执行__new__(对象,参数1,参数2)方法, print('in new function') return object.__new__(A,*args,**kwargs) #2.1、借助object类的__new__方法生成A类并返回给对象; a = A() #1、实例化,结果为:in new function in init function print(a.x) #结果为:1
6.1、画图说明

7、设计模式与单例模式
设计模式:共 23种
单例模式:
一个类 始终 只有 一个 实例
当你第一次实例化这个类的时候 就创建一个实例化的对象
当你之后再来实例化的时候 就用之前创建的对象
单例模式实现
class A: _instance = False def __init__(self,name,age): #3、然后执行__init__方法 self.name = name self.age = age def __new__(cls, *args, **kwargs): #1.1、实例化对象a1的时候首先执行__new__(对象,参数1,参数2)方法,2.1、实例化对象a时首先执行__new__方法 if cls._instance: #1.2、如果_instance为True,2.2、此时cls._instance == A对象,所以为True return cls._instance #1.3、则返回:cls._instance,2.3、返回A对象 cls._instance = object.__new__(A) #1.4、没有匹配到上面的if则执行这里,借助object类的__new__方法创建了一个类,此时_instance == A对象 return cls._instance #1.5、返回cls._instance,此时_instance == A对象 a1 = A('net',23) #1、实例化对象a1 a = A('ouyang',33) #2、实例化对象a a.cloth = '风衣' #给对象a添加cloth属性 print(a1.name) #结果为:ouyang print(a.name) #结果为:ouyang print(a.cloth,a1.cloth) #结果为:风衣 风衣
7.1、画图说明实现过程

8、eq方法
触发条件:==
#eq方法:正常情况下判断两个对象是否相等是比较内存地址,可以通过eq方法进行定制,触发条件为:==(比较两个值) class A: def __init__(self,name): self.name = name def __eq__(self, other): if self.name == other.name: #或者:if self.__dict__ == other.__dict__: return True else: return False ob1 = A('egon') ob2 = A('egon') print(ob1 == ob2) #没有实现eq方法的时候默认就是比较两个对象的内存地址,使用eq方法可以定制比较对象的方法,结果为:True obj1 = A('test') obj2 = A('test1') print(obj1 == obj2) #使用==(比较两个值)时会触发__eq__方法,结果为:False
画图:

9、hash方法(计算hash值)
计算hash值实际是通过对象中的__hash__方法,如果对象里没有__hash__方法才会去父类中继承父类的__hash__方法
class A: def __init__(self,name,sex): self.name = name self.sex = sex def __hash__(self): return hash(self.name + self.sex) a = A('ouyang','男') b = A('ouyang','男') # print(hash(a),hash(b)) #默认情况下根据内存地址进行hash(hash值不一样),那么如何实现同一个类的对象的hash是一样的那? print(hash(a),hash(b)) #使用hash()方法实际是调用的对象里的__hash__方法,如果对象里没有则继承父类里的hash方法,结果为:-5861893281255257818

浙公网安备 33010602011771号