class Testa: pass class Testb(object): pass if __name__ == '__main__': print 'testb = ',dir(Testb) print ' ' print 'testa = ',dir(Testa)
运行结果:
testb = ['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
testa = ['__doc__', '__module__']
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__
运行结果:
{'a': 0, '__module__': '__main__', 'b': 1, 'class_test': <classmethod object at 0x021FF510>, '__dict__': <attribute '__dict__' of 'A' objects>, '__init__': <function __init__ at 0x01E19DB0>, 'test': <function test at 0x022044B0>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': ' Class A. ', 'static_test': <staticmethod object at 0x021FF4D0>}
{'a': 2, 'b': 3}
由此可见,类和实例分别拥有自己的__dict__
类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在类__dict__里的
对象的__dict__中存储了一些self.xxx的一些东西
2、Python里什么没有__dict__属性
虽然说一切皆对象,但对象也有不同,就好比不是每个人的女朋友都是一个人一样,一些内置的数据类型是没有__dict__属性的,如下:
num = 3 ll = [] dd = {} print num.__dict__ print ll.__dict__ print dd.__dict__
当我们运行这样的代码时,解释器就会告诉我们
Traceback (most recent call last): File "f:python est.py", line 54, in <module> print num.__dict__ AttributeError: 'int' object has no attribute '__dict__' Traceback (most recent call last): File "f:python est.py", line 55, in <module> print ll.__dict__ AttributeError: 'list' object has no attribute '__dict__' Traceback (most recent call last): File "f:python est.py", line 56, in <module> print dd.__dict__ AttributeError: 'dict' object has no attribute '__dict__'
int, list, dict等这些常用的数据类型是没有__dict__属性的,其实这是可预料的,就算给了它们dict属性也没啥用,毕竟它们只是用来做数据容器的。
3、发生继承时候的__dict__属性
子类有自己的__dict__, 父类也有自己的__dict__,子类的全局变量和函数放在子类的dict中,父类的放在父类dict中。
class Parent(object): a = 0 b = 1 def __init__(self): self.a = 2 self.b = 3 def p_test(self): pass class Child(Parent): a = 4 b = 5 def __init__(self): super(Child, self).__init__() # self.a = 6 #重写父类的成员变量 # self.b = 7 def c_test(self): pass def p_test(self): pass p = Parent() c = Child() print Parent.__dict__ print Child.__dict__ print p.__dict__ print c.__dict__
运行结果:
{'a': 0, '__module__': '__main__', 'b': 1, '__dict__': <attribute '__dict__' of 'Parent' objects>, 'p_test': <function p_test at 0x021B4530>, '__weakref__': <attribute '__weakref__' of 'Parent' objects>, '__doc__': None, '__init__': <function __init__ at 0x00879DB0>}
{'a': 4, 'c_test': <function c_test at 0x021B44F0>, '__module__': '__main__', 'b': 5, 'p_test': <function p_test at 0x021B4570>, '__doc__': None, '__init__': <function __init__ at 0x021B4470>}
{'a': 2, 'b': 3}
{'a': 2, 'b': 3}
1)上段输出结果中,用红色字体标出的是类变量和函数,由结果可知,每个类的类变量、函数名都放在自己的__dict__中
2) 再来看一下实力变量的__dict__中,由蓝色字体标识,父类和子类对象的__dict__是公用的
class Borg(object): __shared_state = {} #子类和父类共享这个变量,导致输出的结果是一样的,具有迷惑性 def __init__(self): self.__dict__ = self.__shared_state self.state = 'Init' def __str__(self): return self.state class YourBorg(Borg): pass if __name__ == '__main__': rm1 = Borg() rm2 = Borg() rm1.state = 'Idle' rm2.state = 'Running' print('rm1: {0}'.format(rm1)) print('rm2: {0}'.format(rm2))
输出结果:
rm1: Running
rm2: Running
上面这一段代码,乍看挺神奇的,Borg 的各个实例共享了state。实现起来也很巧妙,利用了__dict__。 我们知道,python中__dict__存储了该对象的一些属性。类和实例分别拥有自己的__dict__,且实例会共享类的__dict__。
这里有一个我一直以来都搞混的知识点,在__init__ 中声明的变量 ,以及在方法体之外声明的变量分别是在哪里。很简单的测试就能得到,在__init__中,self.xxx = xxx会把变量存在实例的__dict__中,仅会在该实例中能获取到,
而在方法体外声明的,会在class的__dict__中。
总结:
1) 内置的数据类型没有__dict__属性
2) 每个类有自己的__dict__属性,就算存着继承关系,父类的__dict__ 并不会影响子类的__dict__
3) 对象也有自己的__dict__属性, 存储self.xxx 信息,父子类对象公用__dict__
__xxxitem__:使用 [''] 的方式操作属性时被调用
__setitem__:每当属性被赋值的时候都会调用该方法,因此不能再该方法内赋值 self.name = value 会死循环
__getitem__:当访问属性时会调用该方法
__delitem__:当删除属性时调用该方法
python 运算符重载__getitem__和__setitem__
class Indexer: def __getitem__(self, index): #重载索引,对于实例的索引运算,会自动调用__getitem__ return index**2 x = Indexer() print(x[3]) class Indexer2: data=[1,2,3,4,5] def __getitem__(self, index): #重载索引,对于实例的索引运算,会自动调用__getitem__ print '__getitem__' return self.data[index] def __setitem__(self, index, value): #重载索引赋值,对于实例的索引赋值,会自动调用__setitem__ print 'setitem:' self.data[index]=value def __index__(self): print '__index__' return 255 x=Indexer2() print(x[0]) print x[::-1] print(x[1:]) print(x[2:4]) print(bin(x), bin(255)) print(('a'*256)[x]) x[0] = 102 print x exit(0)
运行结果:
9
__getitem__
1
__getitem__
[5, 4, 3, 2, 1]
__getitem__
[2, 3, 4, 5]
__getitem__
[3, 4]
__index__
('0b11111111', '0b11111111')
__index__
a
setitem:
<__main__.Indexer2 instance at 0x022C3D50>
python中 __setitem__(),__getitem__()
class Person: #将对象当作字典操作,设置键值对时会触发该方法 def __setitem__(self, key, value): print(key,value) self.__dict__[key]=value #将对象当作字典操作,根据键获取值时会触发该方法 def __getitem__(self, item): print(item) return self.__dict__.get(item) #将对象当作字典操作,删除指定的键值对时自动触发 def __delitem__(self, key): del self.__dict__[key] xiaoming = Person() xiaoming['name'] = '小宏' print(xiaoming.__dict__) print(xiaoming['name']) del xiaoming['name']
运行结果:
('name', 'xe5xb0x8fxe5xaex8f')
{'name': 'xe5xb0x8fxe5xaex8f'}
name
小宏
__setitem__(self,key,value):
这个方法应该以与键相关联的方式存储值,以便之后能够使用__setitem__来获取。当然,这个对象可变时才需要实现这个方法。
class Tag: def __init__(self): self.change = {'python': 'This is python', 'php': 'PHP is a good language'} def __getitem__(self, item): print('调用getitem') return self.change[item] def __setitem__(self, key, value): print('调用setitem') self.change[key] = value a = Tag() print(a['php']) a['php'] = 'PHP is not a good language' a['ruby'] = 'Ruby is not a good language' print(a['php']) print(a['ruby']) print a.__dict__ exit(0)
运行结果:
调用getitem
PHP is a good language
调用setitem
调用setitem
调用getitem
PHP is not a good language
调用getitem
Ruby is not a good language
{'change': {'python': 'This is python', 'php': 'PHP is not a good language', 'ruby': 'Ruby is not a good language'}}
__delitem__和__delattr__
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): if item == 1: print('hahaha') # print(self.__dict__[item]) def __setitem__(self, key, value): 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) f1 = Foo('sb') #访问属性的方式变了 #对象名.属性 f1=Foo('sb') f1['age']=18 #给f1添加一个属性 del f1['age'] #删除属性 # f1.name print(f1['name']) f1.__dict__['age'] = 18 f1['age1']=19 del f1.age1 #删除属性 f1['name']='alex' print(f1.__dict__) exit(0)
输出结果:
del obj[key]时,我执行 None del obj.key时,我执行 {'age': 18, 'name': 'alex'}
使用dict时,Key是无序的。在对dict做迭代时,我们无法确定Key的顺序。而如果要保持Key的顺序,可以用OrderedDict
注意,OrderedDict的Key会按照插入的顺序排列,不是Key本身排序:
OrderedDict可以实现一个FIFO(先进先出)的dict,当容量超出限制时,先删除最早添加的Key:
from collections import OrderedDict class LastUpdatedOrderedDict(OrderedDict): def __init__(self, capacity): super(LastUpdatedOrderedDict, self).__init__() self._capacity = capacity def __setitem__(self, key, value): containsKey = 1 if key in self else 0 if len(self) - containsKey >= self._capacity: last = self.popitem(last=False) print 'remove:', last if containsKey: del self[key] print 'set:', (key, value) else: print 'add:', (key, value) OrderedDict.__setitem__(self, key, value) # test d = LastUpdatedOrderedDict(capacity=3) d['A'] = 100 d['B'] = 200 d['C'] = 300 d['D'] = 400 d['E'] = 500 print d
运行结果:
add: ('A', 100)
add: ('B', 200)
add: ('C', 300)
remove: ('A', 100)
add: ('D', 400)
remove: ('B', 200)
add: ('E', 500)
LastUpdatedOrderedDict([('C', 300), ('D', 400), ('E', 500)])
python中__len__
#coding=utf-8 class A: def __init__(self): self.a = 1 self.b = 2 def fun(self): pass def __len__(self): return len(self.__dict__) #返回的是对象自身的长度 a = A() print(len(a)) #2 exit(0)
_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)) #当在外部使用hash()这种方法实际上就是在内部调用 __hash__这个方法 exit(0)
_EQ__
#coding=utf-8 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) #在外部执行==实际上就是在内部调用 __eq__方法 exit(0)
__call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
#coding=utf-8 class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__') obj = Foo() # 执行 __init__ obj() # 执行 __call__ exit(0)
python __setitem__ 和 __setattr__关于无限递归
def __setattr__(self,name,value):
self.name=value
它在出现类似self.a=b的赋值语句时会调用__setattr__并在函数中继续进行同样的赋值操作从而引起无限递归,也就是触发调用__setattr__的条件是遇到self.a=b这样的语句
但对于__setitem__
def __setitem__(self,key,value):
self.__dict__[key]=value
如果定义了一个实例s,要使用序列的方法__setitem__,自然s会成一个字典,而如果我使用s[a]=b的语句修改字典的键的值时为何不会再在__setitem__中继续将a和b作为key和value进行修改赋值从而出现无限递归?
触发调用__setitem__的条件应该是类似s[a]=b还是别的什么语句吗?
def __setitem__(self,key,value): self[key]=value #以这种方式则会触发无限递归 def __setitem__(self,key,value): self.__dict__[key]=value #这种则不会,它是以修改对象内置字典的键的值的方式来修改对象的属性值
参考自:
https://blog.csdn.net/qq_42467563/article/details/82974285
https://www.jianshu.com/p/cf8450b4b537