python中類的發展java
➤截止到python2.1,只存在舊式類。舊式類中,類名和type是無關的:若是x是一箇舊式類,那麼x.__class__定義了x的類名,可是type(x)老是返回<type 'instance'>。這反映了全部的舊式類的實例是經過一個單一的叫作instance的內建類型來實現的,這是它和類不一樣的地方。python
➤新式類是在python2.2爲了統一類和實例引入的。一個新式類只能由用戶自定義。若是x是一個新式類的實例,那麼type(x)和x.__class__是同樣的結果(儘管這不能獲得保證,由於新式類的實例的__class__方法是容許被用戶覆蓋的)。程序員
➤Python 2.x中默認都是經典類,只有顯式繼承了object纔是新式類算法
➤Python 3.x中默認都是新式類,經典類被移除,沒必要顯式的繼承object數組
新式類與經典類的區別數據結構
➤新式類都從object繼承,經典類不須要。app
➤新式類相同父類只執行一次構造函數,經典類重複執行屢次。函數
➤區分方式:type(classname)工具
-新式類:type優化
-經典類: classobj
➤新式類的MRO(method resolution order 基類搜索順序)算法採用C3算法廣度優先搜索,而舊式類的MRO算法是採用深度優先搜索
#經典類 class A: def __init__(self): print 'this is A' def save(self): print 'come from A' class B(A): def __init__(self): print 'this is B' class C(A): def __init__(self): print 'this is C' def save(self): print 'come from C' class D(B,C): def __init__(self): print 'this is D' d1=D() d1.save() #結果爲'come from A
#新式類
class A(object): def __init__(self): print 'this is A'
def save(self): print 'come from A'
class B(A): def __init__(self): print 'this is B'
class C(A): def __init__(self): print 'this is C'
def save(self): print 'come from C'
class D(B,C): def __init__(self): print 'this is D' d1=D() d1.save() #結果爲'come from C'
類會產生新的名稱空間,用來存放類的變量名與函數名,能夠經過類名.__dict__查看
對於經典類來講咱們能夠經過該字典操做類名稱空間的名字(新式類有限制),但python爲咱們提供專門的.語法,點是訪問屬性的語法,類中定義的名字,都是類的屬性
# .專門用來訪問屬性,本質操做的就是__dict__ print(type(OldClass), OldClass.__dict__['school']) # 等於經典類的操做OldboyStudent.__dict__['school']
print(type(NewClass), NewClass.__dict__['school'])
OldClass.school='Oldboy123' # 等於經典類的操做OldboyStudent.__dict__['school']='Oldboy' print(OldClass.school) # OldClass.x=1 #等於經典類的操做OldboyStudent.__dict__['x']=1 # del OldboyStudent.x #等於經典類的操做OldboyStudent.__dict__.pop('x')
(<type 'classobj'>, 'oldboy')
(<type 'type'>, 'oldboy')
Oldboy123
''' 1.__slots__是什麼:是一個類變量,變量值能夠是列表,元祖,或者可迭代對象,也能夠是一個字符串(意味着全部實例只有一個數據屬性) 2.引子:使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每一個實例的是獨立的) 3.爲什麼使用__slots__:字典會佔用大量內存,若是你有一個屬性不多的類,可是有不少實例,爲了節省內存可使用__slots__取代實例的__dict__ 當你定義__slots__後,__slots__就會爲實例使用一種更加緊湊的內部表示。實例經過一個很小的固定大小的數組來構建,而不是爲每一個實例定義一個 字典,這跟元組或列表很相似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個很差的地方就是咱們不能再給 實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。 4.注意事項:__slots__的不少特性都依賴於普通的基於字典的實現。另外,定義了__slots__後的類再也不 支持一些普通類特性了,好比多繼承。大多數狀況下,你應該 只在那些常常被使用到 的用做數據結構的類上定義__slots__好比在程序中須要建立某個類的幾百萬個實例對象 。 關於__slots__的一個常見誤區是它能夠做爲一個封裝工具來防止用戶給實例增長新的屬性。儘管使用__slots__能夠達到這樣的目的,可是這個並非它的初衷。 更多的是用來做爲一個內存優化工具。 ''' class Foo: __slots__='x' f1=Foo() f1.x=1 f1.y=2#報錯 print(f1.__slots__) #f1再也不有__dict__ class Bar: __slots__=['x','y'] n=Bar() n.x,n.y=1,2 n.z=3#報錯 __slots__使用
# Tools:Pycharm 2017.3.2 # author ="wlx" __date__ = '2018/8/14 17:37' #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' format_dict={ 'nat':'{obj.name}-{obj.addr}-{obj.type}',#學校名-學校地址-學校類型 'tna':'{obj.type}:{obj.name}:{obj.addr}',#學校類型:學校名:學校地址 'tan':'{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='nat' 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,'nat')) print(format(s1,'tna')) print(format(s1,'tan')) print(format(s1,'asfdasdffd'))
注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()() class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print '__call__' obj = Foo() # 執行 __init__ obj() # 執行 __call__
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): 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') print(f1.__dict__) f1['age']=18 # 自動調用setitem f1['age1']=19 print(f1.__dict__) res = f1['age'] # 自動調用getitem del f1.age1 # 自動調用delattr print(f1.__dict__) del f1['age'] # 自動調用delitem f1['name']='alex' print(f1.__dict__) ############## {'name': 'sb'} {'name': 'sb', 'age': 18, 'age1': 19} 18 del obj.key時,我執行 {'name': 'sb', 'age': 18} del obj[key]時,我執行 {'name': 'alex'}
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的屬性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #這就無限遞歸了,你好好想一想 # self.__dict__[key]=value #應該使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #無限遞歸了 self.__dict__.pop(item) #__setattr__添加/修改屬性會觸發它的執行 f1=Foo(10) print(f1.__dict__) # 由於你重寫了__setattr__,凡是賦值操做都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操做屬性字典,不然永遠沒法賦值 f1.z=3 print(f1.__dict__) #__delattr__刪除屬性的時候會觸發 f1.__dict__['a']=3#咱們能夠直接修改屬性字典,來完成添加/修改屬性的操做 del f1.a print(f1.__dict__) #__getattr__只有在使用點調用屬性且屬性不存在的時候纔會觸發 f1.xxxxxx 三者的用法演示
hasattr,getattr,setattr,delattr
class BlackMedium: feature = 'Ugly' def __init__(self, name, addr): self.name = name self.addr = addr def sell_house(self): print('%s 黑中介賣房子啦,傻逼纔買呢,可是誰能證實本身不傻逼' % self.name) def rent_house(self): print('%s 黑中介租房子啦,傻逼才租呢' % self.name) b1 = BlackMedium('萬成置地', '回龍觀天露園') # 檢測是否含有某屬性 print(hasattr(b1, 'name')) print(hasattr(b1, 'sell_house')) print(hasattr(b1, 'age')) # 獲取屬性 n = getattr(b1, 'name') print(n) func = getattr(b1, 'rent_house') func() # getattr(b1,'aaaaaaaa') #報錯 print(getattr(b1, 'aaaaaaaa', '不存在啊')) # 設置屬性 setattr(b1, 'sb', True) setattr(b1, 'show_name', lambda self: self.name + 'sb') print(b1.__dict__) print(b1.show_name(b1)) # 刪除屬性 delattr(b1, 'addr') delattr(b1, 'show_name') delattr(b1, 'show_name111') # 不存在,則報錯 print(b1.__dict__)
模塊中使用反射
#!/usr/bin/env python # -*- coding:utf-8 -*- import sys def s1(): print 's1' def s2(): print 's2' this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2') 反射當前模塊成員
構造函數的做用爲:初始化類內的各類屬性
構造函數示例:
class Role(object): #定義一個類, class是定義類的語法,Role是類名 def __init__(self,name,role,weapon,life_value=100,money=15000): #初始化函數,在生成一個角色時要初始化的一些屬性就填寫在這裏 self.name = name #__init__中的第一個參數self,和這裏的self都 是什麼意思? 看下面解釋 self.role = role self.weapon = weapon self.life_value = life_value self.money = money def sayhi(self): # 爲何類內定義的函數第一個參數都是self?有什麼做用? print("hello,I am a dog, my name is ",self.name)
如何理解self和實例化過程?
理解self和實例化的過程,要先了解實例化的幾種方法。例如java中,java的實例化以下圖
當main方法結束以後p就沒有了,指向堆內存的地址就斷開了,堆內存就釋放了
在java的實例化過程當中,建立對象語句先進對內存,成員變量置空,而後調用構造函數,初始化成員變量,而後把內存給棧內的對象p
相比,在python中,實例化對象並無採用java式的實例化方式,下面講解python實例化方式和self的做用,有下面兩個實例化語句:
實例化時至關於給__init__()函數傳參,self自動傳,不須要手動寫。
r1 = Role('Alex','police','AK47’) #此時self 至關於 r1 , Role(r1,'Alex','police','AK47’) r2 = Role('Jack','terrorist','B22’)#此時self 至關於 r2, Role(r2,'Jack','terrorist','B22’)
這裏你必定會問爲何會自動傳一個self參數呢,不是像java那樣吧實例化後的地址給r1麼?
如今咱們知道了,實例化時經過構造函數,已經完成了成員變量和對象之間的關聯,可是,對象裏面尚未關聯函數(方法)啊?
其實這裏並無給對象r1關聯方法,而是用到類的方法時把r1(self)傳給該函數,而後經過函數裏的self.xxx進行使用,以下:
def buy_gun(self,gun_name): print(「%s has just bought %s」 %(self.name,gun_name) ) r1 = Role('Alex','police','AK47') r1.buy_gun("B21」) #python 會自動幫你轉成 Role.buy_gun(r1,」B21")
執行結果
#Alex has just bought B21
類變量:類變量直接定義在類中且在函數體以外,類變量在整個實例化的對象中是公用的。
實例變量:定義在方法中的變量,只做用於當前實例。(對象調用時,實例變量和類變量重名以實例變量爲先)
obj.name會先從obj本身的名稱空間裏找name,找不到則去類中找,類也找不到就找父類...最後都找不到就拋出異常
class Role: n = 123 # 類變量 n_list = [] name = "我是類name" def __init__(self, name, role, weapon, life_value=100, money=15000): # 構造函數 # 在實例化時作一些類的初始化的工做 self.name = name # r1.name=name實例變量(靜態屬性),做用域就是實例自己 self.role = role self.weapon = weapon self.__life_value = life_value self.money = money def __del__(self): pass # print("%s 完全死了。。。。" %self.name) r1 = Role('Chenronghua', 'police', 'AK47') r2 = Role('jack', 'terrorist', 'B22')
看到這裏時,你應該明白了python實例化時作了什麼,類變量和實例變量的區別,那麼問個問題來檢驗一下,在實例化以後,對對象r1賦一個新實例變量,是否符合語法?
r1.n_list.append('123') r2.n_list.append('456') print(r2.n_list) # ['123', '456']
注意這種.append()形式並無對r1建立新的實例變量,而是直接對類變量n_list進行操做
可是下面這種則是建立了r1的新實例變量
r1.n_list = ['879'] r2.n_list.append('456') print(r2.n_list) # ['456']
這種形式,至關於建立了r1的新實例變量,對類變量n_list沒有影響
你覺得你理解了麼,試試下面的代碼,看看輸出和你預料的是否是同樣的:
class D: a = 10 def __init__(self): self.b = 10 d1 = D() d2 = D() d1.a += 1 print(d1.a, d2.a) D.a = 100 print(d1.a, d2.a)
這兩個print的輸出都是什麼?結果以下:
11 10 11 100
若是你的預料的不對,去上面幾行找答案吧
不推薦使用,知道便可
class A: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def speak(self): # 綁定和參數有關的函數 print('%s is roaring' %self.name) def call(): # 綁定和參數無關的函數 print('someone is roaring') a = A('wei', 18, 'male') a.speak = speak a.speak(a) # 參數有關,要傳self(對象自身) a.call = call a.call() print(a.__dict__) ################ wei is roaring someone is roaring {'name': 'wei', 'age': 18, 'sex': 'male', 'speak': <function speak at 0x000002689C702E18>, 'call': <function call at 0x00000268ABDBE268>}