定义:双下线方法是类的特殊方法,是由双下划线加方法名加双下划线 方法名的具有特殊意义的方法,双下方法主要是python源码程序员使用的,我们在开发中尽量不要使用双下方法,但是深入研究双下方法,更有益于我们阅读源码。

调用:不同的双下方法有不同的触发方式,就好比盗墓时触发的机关一样,不知不觉就触发了双下方法,例如:init

以下几个只需大概知道的双下线方法

len方法

class A: def __len__(self): print(666) return 3 a = A() print(len(a)) # len 一个对象就会触发这个对象的 __len__方法。 class B: def __init__(self): self.a = 1 self.b = 2 def __len__(self): return len(self.__dict__) b = B() print(len(b))

hash方法

class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): print("hash method.") return hash(str(self.a)+str(self.b)) a = A() print(hash(a))

eq方法

class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self,obj): print("call eq method.") if self.a == obj.a and self.b == obj.b: return True a = A() b = A() print(a == b)

item系列

可以把一个对象变成dict, 可以像dict一样增删改查

class Brand: def __init__(self,name): self.name=name def __getitem__(self, item): print("获取KEY",item) print(self.__dict__[item]) def __setitem__(self, key, value): print("设置一个key...",key) self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') self.__dict__.pop(key) def __delattr__(self, item): print('del obj.key时,我执行') self.__dict__.pop(item) b=Brand('小猿圈') b["slogan"] = "自学编程谁不爱小猿圈" b["website"] = "apeland.cn" del b["website"] b['name']='小猿圈Apeland' b["name"] # 获取KEY print(b.__dict__)

需要重点会的双下线方法

str & repr

这2个方法比较像,均可改变对象的字符串显示格式

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) s1=School('小猿圈','北京','私立') print('from repr: ',repr(s1)) print('from str: ',str(s1)) print(s1) ''' str函数或者print函数调用时--->obj.__str__() repr或者交互式解释器中调用时--->obj.__repr__() 如果__str__没有被定义,那么就会使用__repr__来代替输出 注意:这俩方法的返回值必须是字符串,否则抛出异常 '''

del 析构方法

析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

class Foo: def __del__(self): print('执行我啦') f1=Foo() del f1 print('------->') #执行输出 执行我啦 ------->

new方法

我们知道实例化init会自动执行, 其实在init方法之前,还有一个new方法也会自动执行,你可以在new里执行一些实例化前的定制动作

class Person(object): def __init__(self,name): self.name = name print("--init ....") def __new__(cls, *args, **kwargs): """ cls : 代表Person这个类本身 :param args: :param kwargs: :return: """ print("--in new: ",cls,*args,**kwargs) return object.__new__(cls) # 调用父类的__new__方法,必须这么干 ,要不然__init__方法就不会执行了 p = Person("Alex") print(p.name) print(Person)

用new方法实现单例模式

单例模式:

单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

什么情况下用单例?

对于系统中的某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源;如果这些窗口显示的内容不一致,则意味着在某一瞬间系统有多个状态,与实际不符,也会给用户带来误解,不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。

如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。

class Printer(object): __instance = None # 用来存唯一的一个实例 __tasks = [] def __init__(self,task): self.__tasks.append(task) print("added a new task in queue..",task) def __new__(cls, *args, **kwargs): if cls.__instance is None: # 代表之前还没被实例化过 obj = object.__new__(cls) cls.__instance = obj # 把第一次实例化的对象 存下来,以后每次实例化都用这个第一次的对象 return cls.__instance # 下一次实例化时,就返回第一次实例化的对象 def jobs(self): return self.__tasks job = Printer("job1 word") job2 = Printer("job2 png") job3 = Printer("job3 excel") print(id(job),id(job2),id(job3)) # 会发现这3个实例的内存id一样 print(job3.jobs())

call方法

对象后面加括号,触发执行。

注:构造方法new的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Person(object): def __init__(self,name): self.name = name print("--init ....") def __new__(cls, *args, **kwargs): """ cls : 代表Person这个类本身 :param args: :param kwargs: :return: """ print("--in new: ",cls,*args,**kwargs) return object.__new__(cls) # 调用父类的__new__方法,必须这么干 ,要不然__init__方法就不会执行了 def __call__(self, *args, **kwargs): print("-->call",self,*args,**kwargs) p = Person("Alex") p() # 此时会执行__call__