class Testa: pass class Testb(object): pass if __name__ == '__main__': print 'testb = ',dir(Testb) print '\n' 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__': '\n Class A.\n ', '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\test.py", line 54, in <module> print num.__dict__ AttributeError: 'int' object has no attribute '__dict__' Traceback (most recent call last): File "f:\python\test.py", line 55, in <module> print ll.__dict__ AttributeError: 'list' object has no attribute '__dict__' Traceback (most recent call last): File "f:\python\test.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', '\xe5\xb0\x8f\xe5\xae\x8f')
{'name': '\xe5\xb0\x8f\xe5\xae\x8f'}
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