python中dir,__dict__ , __setitem__(),__getitem__()


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__關於無限遞歸

對於__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

https://www.cnblogs.com/alvin2010/p/9102344.html

https://blog.csdn.net/xhw88398569/article/details/48690163


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM