__str__和__repr__,__format__
改变对象的字符串显示__str__,__repr__
自定制格式化字符串__format__
#_*_coding:utf-8_*_ format_dict={ '格式1':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 '格式2':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 '格式3':'{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='格式1' 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,'格式1')) print(format(s1,'格式2')) print(format(s1,'格式3')) print(format(s1,'xxx'))
class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B' b=B() print('%s'%b) print('%r'%b)
__del__
析构方:当对象在内存中被释放的同时自动触发执行该方法。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): print('执行我啦') f1=Foo() del f1 # 执行我啦 print('------->') f1 # NameError: name 'f1' is not defined
item系列
__getitem__\__setitem__\__delitem__
==》__dict__
在Python中,如果我们想实现创建类似于序列和映射的类,可以通过重写魔法方法__getitem__、__setitem__、__delitem__方法去模拟。
这些魔术方法的原理就是:当我们对类的属性item进行下标的操作时,首先会被__getitem__()、__setitem__()、__delitem__()拦截,从而进行我们在方法中设定的操作,如赋值,修改内容,删除内容等等。
对类的属性item进行下标的操作==>obj["属性"]
class A: def __init__(self,name): self.name=name def __getitem__(self, item): print("呵呵你看不到") def __setitem__(self, key, value): self.__dict__[key]=value print('设置了 obj 属性 [%s] 为 %s'%(key,value)) def __delitem__(self, key): print('del obj[%s]时,我执行'%key) self.__dict__.pop(key) def __delattr__(self, item): print('del obj.%s时,我执行'%item) self.__dict__.pop(item) def __setattr__(self, item,value): print("调用了__setattr__方法") self.__dict__[item]=value def daren(self): pass a=A('斌哥') #这个过程也调用了__setattr__方法 print(a.__dict__) # {'name': '斌哥'} a.name #==>'斌哥' print(a['name']) #调用了__getitem__方法 # 呵呵你看不到 # None a['age']=18 #=触发=__setitem__>设置了 obj 属性 [age] 为 18 a.age=19 #=触发=__setitem__>调用了__setattr__方法 a['appear']='很帅' # #=触发=>设置了 obj 属性 [appear] 为 很帅 del a.appear #触发__delattr__==>del obj.appear时,我执行 del a['age'] #触发__delitem__==>del obj[age]时,我执行 a.school='nc' print(a.__dict__) # {'school': 'nc', 'name': '斌哥'} # 结论:感觉属性维护了一个doc字典 # 结论 : obj.属性 调用 使用的是xxattr()方法 #xxitem()方法提供了 访问属性一个【】接口
__dict__
class A: def __init__(self,name): self.name=name def __len__(self): return len(self) a=A('斌哥') print(A.__dict__) # {'__doc__': None, '__module__': '__main__', '__len__': <function A.__len__ at 0x0000014FF1C71EA0>, # '__init__': <function A.__init__ at 0x0000014FF1BE1730>, '__weakref__': <attribute '__weakref__' of 'A' objects>, # '__dict__': <attribute '__dict__' of 'A' objects>} print(a.__dict__) # {'name': '斌哥'} class B(A): def __init__(self,age,name): super().__init__(name) self.age=age def __len__(self): return len(self) b=B(18,"bb") print(B.__dict__) print(b.__dict__) # {'__doc__': None, '__module__': '__main__', # '__init__': <function B.__init__ at 0x0000014FF1CB1A60>, # '__len__': <function B.__len__ at 0x0000014FF1CB1730>} # # {'name': 'bb', 'age': 18}
# -*- coding: utf-8 -*- class A(object): """ Class A. """ a = 0 b = 1 def __init__(self): self.a = 2 self.b = 3 def test(self): print 'a normal func.' @staticmethod def static_test(self): print 'a static func.' @classmethod def class_test(self): print 'a calss func.' obj = A() print(A.__dict__) print(obj.__dict__)
1.类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里的
2.对象的属性(不含类的)保存在实例__dict___里
3.子类有自己的__dict__, 父类也有自己的__dict__,子类的全局变量和函数放在子类的dict中,父类的放在父类dict中。对象也这样。
4.内置的数据类型没有__dict__属性
__init__和__new__方法
__new__() 是在新式类中新出现的方法,它作用在构造方法建造实例之前,可以这么理解,在 Python 中存在于类里面的构造方法 __init__() 负责将类的实例化,而在 __init__() 启动之前,__new__() 决定是否要使用该 __init__() 方法,因为__new__() 可以调用其他类的构造方法或者直接返回别的对象来作为本类的实例。
如果将类比喻为工厂,那么__init__()方法则是该工厂的生产工人,__init__()方法接受的初始化参数则是生产所需原料,__init__()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而__new__()则是生产部经理,__new__()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。
_new__() 方法的特性: __new__() 方法是在类准备将自身实例化时调用。 __new__() 方法始终都是类的静态方法,即使没有被加上静态方法装饰器。
class A: def __init__(self): self.x = 1 print('in A init function') def __new__(cls, *args, **kwargs): print('in A new function') # return object.__new__(A) return super(A,cls).__new__(cls) a = A() print(a.x)
in A new function
in A init function
1
说明
1、继承自object的新式类才有__new__
2、__new__至少要有一个参数cls,代表当前类,此参数在实例化时由Python解释器自动识别
3、__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类(通过super(当前类名, cls))__new__出来的实例,或者直接是object的__new__出来的实例
4、__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
5、如果__new__创建的是当前类的实例,会自动调用__init__函数,通过return语句里面调用的__new__函数的第一个参数是cls来保证是当前类实例,如果是其他类的类名,;那么实际创建返回的就是其他类的实例,其实就不会调用当前类的__init__函数,也不会调用其他类的__init__函数。
class A: def __init__(self): self.x = 1 print('in A init function') def __new__(cls, *args, **kwargs): print('in A new function') # return object.__new__(A) return super(A,cls).__new__(cls) a = A() print(a.x)
说明
1、在定义子类时没有重新定义__new__()时,Python默认是调用该类的直接父类的__new__()方法来构造该类的实例,如果该类的父类也没有重写__new__(),那么将一直按此规矩追溯至object的__new__()方法,因为object是所有新式类的基类。
2、而如果子类中重写了__new__()方法,那么你可以自由选择任意一个的其他的新式类(必定要是新式类,只有新式类必定都有__new__(),因为所有新式类都是object的后代,而经典类则没有__new__()方法)的__new__()方法来制造实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环。反正肯定不能调用自己的__new__,这肯定是死循环。
3、对于子类的__init__,其调用规则跟__new__是一致的,当然如果子类和父类的__init__函数都想调用,可以在子类的__init__函数中加入对父类__init__函数的调用。
4、我们在使用时,尽量使用__init__函数,不要去自定义__new__函数,因为这两者在继承派生时的特性还是很不一样的。
__new__ 的作用
1、__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。
假如我们需要一个永远都是正数的整数类型,通过集成int,我们可能会写出这样的代码。
class PositiveInteger(int): def __init__(self, value): super(PositiveInteger, self).__init__(self, abs(value)) i = PositiveInteger(-3) print(i)
然后报错了
这是因为对于int这种 不可变的对象,我们只有重载它的__new__方法才能起到自定义的作用。
class PositiveInteger(int): def __new__(cls, value): return super(PositiveInteger, cls).__new__(cls, abs(value)) i = PositiveInteger(-3) print(i)
通过重载__new__方法,我们实现了需要的功能。
实现单例
class Singleton(object): def __new__(cls): # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象 if not hasattr(cls, 'instance'): cls.instance = super(Singleton, cls).__new__(cls) return cls.instance obj1 = Singleton() obj2 = Singleton() print(obj1 is obj2) #true
class Singleton(A): __instance = None #Call to __init__ of super class is missed def __init__(self, *args, **kwargs): print(2) def __new__(cls, *args, **kwargs): if not cls.__instance: # if not hasattr(cls, 'instance'): cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs) cls.__instance.aa = args[0] print(type(cls), type(cls.__instance), type(cls.__instance.aa)) return cls.__instance obj1 = Singleton(1, 2, 3, b=2) obj2 = Singleton(1, 2, 3, b=2) print(obj1==obj2)
in A new function
<class 'type'> <class '__main__.Singleton'> <class 'int'>
2
2
True
===>有两个2看来即使是单例模式不实例化新的 __init__也会被触发
__call__方法
对象后面加括号,触发执行。
对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class C: def __init__(self): print("__init__") def __call__(self, *args, **kwargs): print('__call__') obj = C() # 执行 __init__ obj() # 执行 __call__
为什么函数对象可以触发?
f = abs print(dir(f))
__len__
果一个类表现得像一个list,要获取有多少个元素,就得用 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))
#2
__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))
class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): # return str(self.a)+'哈哈'+str(self.b) return int(str(1024)+str(self.a)+str(self.b)) a = A() print(hash(a)) # TypeError: __hash__ method should return an integer
__eq__
__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) #true
== 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。这里比较的并非是同一片叶子,可能叶子的种类或者脉络相同就可以了。默认会调用对象的 __eq__()方法。