__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('清华','北京','公立') 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__
发现dict是一个mappingproxy类型,为何不是一个简单的python dict呢?
>>> class A(object): pass ... >>> A.__dict__ mappingproxy({'__module__': '__main__', '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
不清楚,回头研究下。
Python的实例有自己的__dict__,它对应的类也有自己的__dict__ (但是有些特殊的对象是没有__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}
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(a): print('a static func.'+a) @classmethod def class_test(cls): print('a calss func.') obj = A() print(A.__dict__) print(obj.__dict__) A.static_test("1") A.class_test()
1.类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里的
2.对象的属性(不含类的)保存在实例__dict___里
3.子类有自己的__dict__, 父类也有自己的__dict__,子类的全局变量和函数放在子类的dict中,父类的放在父类dict中。对象也这样。
4.内置的数据类型没有__dict__属性
__dir__
dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息。
dir([object])
参数说明:
- object -- 对象、变量、类型。
class A(object): pass a=A() a.name="wqbin" print(A.__dict__) print(a.__dict__) print(a.__dir__())
Note:dir是最大范围的收集一个类或者一个对象的属性,所以是包含__dict__.keys
__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__()方法。