面向对象之反射,双下方法
反射定义:通过字符串的形式操作对象相关的属性,python中的一切事物都是对象(所以都可以使用反射)
hasattr(对象,字符串) 判断这个对象是否有字符串
getattr(对象,字符串)
setattr(对象,字符串)
delattr(对象,字符串)
从对象的角度反射:
lass Foo: f='类的静态变量' def __init__(self,name,age): self.name=name self.age=age def say_hi(self): print(f'hi,{self.name}') obj=Foo('egon',73) # 判断是否含有某种属性 print(hasattr(obj,'name')) hasattr(对象,字符串,返回值) # 返回值可以自己设定 print(hasattr(obj,'say_hi')) # 获取属性 n=getattr(obj,'name') print(n) # 获取字符串的属性名所对应的值 func=getattr(obj,'say_hi') print(func) # 获取该字符串函数名所对应的内存地址 func() # 加上()可以直接调用该函数
对类的角度研究反射
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'))
当前脚本研究的反射
def func1(): print('in func1') def func2(): print('in func2') def func3(): print('in func3') l1=[f'func{i}' for i in range(1,4)] import sys this_modues=sys.modules[__name__] for i in l1: if hasattr(this_moudues,i) getattr(this_moudues,i)()
通过其他模块研究反射
其他模块:other_modules.py def test(): print('in test') import other_modules as obj obj.test() getattr(obj,'test')()
函数vs方法
1.通过函数名,可以大致判断
def func(): pass class A: def func(self): pass a=A() print(func) # 函数 print(a.func) # 对象调用func是方法 print(A.func) # 类调用funcs是函数
2.通过模块
from types import MethodType from types import FunctionType def func(): pass class A: def func(self): pass a=A() print(isinstance(func,MethodType)) # False print(isinstance(func,FunctionType)) # True print(isinstance(A.func,MethodType)) # False print(isinstance(A.func,FunctionType)) # True 类调用funcs是函数 print(isinstance(a.func,MethodType)) # True print(isinstance(a.func,FunctionType)) # False 对象调用funcs是函数 类名调用func是函数,对象调用func时是方法
函数与方法的区别:
(1)函数的是显式传递数据的。如我们要指明为len()函数传递一些要处理数据。
(2)函数则跟对象无关。
(3)方法中的数据则是隐式传递的。
(4)方法可以操作类内部的数据。
(5)方法跟对象是关联的。如我们在用strip()方法是,是不是都是要通过str对象调用 比如我们有字符串s,然后s.strip()这样调用。是的,strip()方法属于str对象。
双下方法:
1.定义: 双下方法是特殊方法,他是解释器提供的,由双下划线加方法名加双下划线(__方法名__)组成具有特殊意义的方法,双下方法主要是python源码程序员使用的
2.调用: 不同的双下方法有不同的触发方式,就好比盗墓时触发的机关一样,不知不觉就触发了双下方法,例如:__init__
__ len__ :当使用len()函数的时候自动触发
class A: def __init__(self,name,age): self.name=name self.age=age def __len__(self): print('触发了__len__方法') return len(self.__dict__) obj=A('MC亚伟',18) ret=len(obj) # len触发了__len__,必须要有返回值,且必须是数字 # 一个对象之所以可以使用len()函数,根本原因是这个对象从属的这个类中有__len__方法 print(ret)
__ hash__:当使用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)) #返回一个哈希值
__str__
触发条件:
1.print()的时候
2.使用f' '或者%s的时候
3.str()的时候
__str__ 打印实例 __repr__打印实例,如果同时有__str__和__repr__时,会优先执行__str__方法 class A: def __init__(self): self.name='太白' def __str__(self): return self.name obj=A() print(str(obj)) class Student: def __init__(self,name,age,sex): print(obj)会先触发__str__方法
__repr__和 __str__相似,但是优先级比__str__低
class A: def __init__(self): pass def __repr__(self): return '小马' a = A() print(repr(a)) print(a) # 打印对象时,也可以调用__repr__方法 print(f'{a}') # 格式化输出,也可以调用__repr__方法 print('%r' % a) # 指定调用__repr__方法
__call __
触发条件:对象后面加括号自动触发
注:构造方法__new__的执行是由创建对象触发的,即:对象 = 类名(),而对于__call__方法的执行是由对象后加括号触发的,即:对象(),或者类()()
__call__ # 对象加括号会自动触发__call__方法 class A: def __init__(self): print(111) def __call__(self, *args, **kwargs): print('执行__call__') return '执行__call__' obj=A() print(obj())
__ del__析构方法 、
触发条件:引用次数为零的时候触发
析构方法,当对象在内存中被释放时,自动触发执行。 注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放, 因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
__ 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)
__ enter__ 和 __ exit__
class A: def __init__(self, text): self.text = text def __enter__(self): # 第二步:执行下面代码, print('in __enter__') self.text = self.text + '您来啦' return self # 将实例化的对象返回f1 def __exit__(self, exc_type, exc_val, exc_tb): # 第四步:f1句柄下的代码执行完毕后,自动执行__exit__方法 self.text = self.text + '这就走啦' print('in exit') with A('大爷') as f1: # 第一步:这里触发了__enter__方法 # pass print(f1.text) # 第三步:f1=self=对象,可以对这个对象进行操作 print(f1.text)
item系列
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(item) print('get 的时候执行我') def __setitem__(self, key, value): self.__dict__[key]=value print('set 的时候执行我') def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) f1=Foo('sb') print(f1.__dict__) f1['name'] # 查看的时候出发__getitem__ f1['age']=10 # 创建的时候触发__setitem__ del f1['age'] # 删除的时候出发__delitem__ print(f1.__dict__)
__ new__(单例模式:面试必考)
__ new__ 创造并返回一个新的方法
单例模式:面试必考
一个类只能实例化一个对象,无论你实例化多少次,内存中只有一个对象
class A: def __init__(self): print('in __init__') def __new__(cls, *args, **kwargs): # print(cls) print('in __new__') object1=object.__new__(cls) # 2.利用object类的__new__产生一个对象空间 return object1 # 3.将这个对象空间返回给A()即obj obj=A() # 1.类名+() 先触发__new__并且将类自动传给cls print(obj)
单例模式:
单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例类的特殊类.通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源.如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案.
采用单例模式动机,原因: 对于系统中的某些类来说,只有一个实例很重要.例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器.如在Windows中就只能打开一个任务管理器.如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象.浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态.因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要.
如何保证一个类只有一个实例并且这个实例易于被访问呢? 定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象.一个更好的解决办法是让类自身负责保存它的唯一实例.这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法.这就是单例模式的模式动机.
单例模式优缺点:
优点:
1.实例控制单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例.
2.灵活性,因为类控制了实例化过程,所以类可以灵活更改实例化过程.
缺点 :
1.开销 ,虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销.可以通过使用静态初始化解决此问题.
2.可能的开发混淆 ,使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例:化对象.因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类.
3.对象生存期 ,不能解决删除单个对象的问题.
class A: istance=None def __init__(self,name): self.name=name def __new__(cls, *args, **kwargs): if not cls.istance: cls.istance=object.__new__(cls) return cls.istance a=A('akdj') a1=A('akdj') a2=A('akdj') print(a) print(a1) print(a2)

浙公网安备 33010602011771号