Python Day7
阿新 • • 發佈:2017-05-20
ror 又是 調用 main 靜態方法 rop pass val 索引操作
b.字段包括什麽? 字段包括:普通字段和靜態字段,它們之間最本質的區別就是內存中保存的位置不同
總結:
b.方法包括什麽? 方法包括: 普通方法、靜態方法和類方法,三種方法都保存在類中,只是調用方式不同。
b.屬性的基本使用
c.屬性的兩種定義方式
私有成員和公有成員的定義不同:私有成員命名時,前兩個字符是下劃線。(特殊成員除外,例如:__init__、__call__、__dict__等)
私有成員和公有成員的訪問限制不同:
二、具體有哪些特殊成員呢? 1. __doc__ 表示類的描述信息
一、類的成員
類的成員可以分為三大類:字段、方法和屬性
註:所有成員中,只有普通字段的內容保存對象中,即:根據此類創建了多少對象,在內存中就有多少個普通字段。而其他的成員,則都是保存在類中,即:無論對象的多少,在內存中只創建一份。
1.字段
a.字段是什麽? 就是變量,用來存東西的b.字段包括什麽? 字段包括:普通字段和靜態字段,它們之間最本質的區別就是內存中保存的位置不同
- 普通字段保存在對象中
- 靜態字段保存在類中
1 class Person(object): 2 3 planet = "earth" 4 5 def __init__(self, name, age, sex): 6 self.name = name 7 self.age = age 8 self.sex = sex 9 10 # 普通字段訪問 11 p1 = Person("Breakering", 25, "M") 12 print(p1.name) 13 # ==>Breakering 14 # print(Person.name) # 普通字段是不能通過類來訪問的 15 16 # 靜態字段訪問 17 print(Person.planet) 18 # ==>earth 19 print(p1.planet) # 可以通過對象來訪問靜態字段 20 # ==>earth 21 p1.planet = "mars" # 此時是在p1這個對象中重新創建了一個變量planet 22 print(p1.planet) 23 # ==>mars 24 print(Person.planet) # 類中的planet不會發生改變 25 # ==>earth 26 Person.planet = "mars" # 只能通過類來修改靜態字段 27 print(Person.planet) 28 # ==>mars
由上述代碼可以看出【普通字段需要通過對象來訪問】【靜態字段通過類訪問】,在使用上可以看出普通字段和靜態字段的歸屬是不同的。其在內容的存儲方式類似如下圖:
總結:
- 靜態字段在內存中只保存一份
- 普通字段在每個對象中都要保存一份
- 普通字段只能通過對象調用,類無法訪問普通字段
- 靜態對象類和對象都能訪問,但是對象在修改靜態字段時本質是重新創建了一個和靜態字段同名的普通字段,因此通過對象無法修改靜態字段
2.方法
a.方法是什麽? 本質上是函數,只不過封裝在類中b.方法包括什麽? 方法包括: 普通方法、靜態方法和類方法,三種方法都保存在類中,只是調用方式不同。
- 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動將調用該方法的對象賦值給self;
- 類方法:由類調用; 至少一個cls參數;執行類方法時,自動將調用該方法的類賦值給cls;
- 靜態方法:由類調用;無默認參數。
1 class Person(object): 2 3 _planet = "earth" 4 5 def __init__(self, name): 6 self.name = name 7 8 def talk(self): # 普通方法 9 print("%s is say Hello!" % self.name) 10 11 @classmethod 12 def get_planet(cls): # 類方法 13 return cls._planet 14 15 @staticmethod 16 def test(): 17 print("test") 18 19 # 調用普通方法 20 p1 = Person("Breakering") 21 p1.talk() 22 print(p1.get_planet()) # 對象可以調用類方法 23 p1.test() # 對象也可以調用靜態方法 24 25 # 調用類方法 26 print(Person.get_planet()) 27 28 # 調用靜態方法 29 Person.test() 30 31 Person.talk() # 類不可直接調用普通方法 32 # ==>TypeError: talk() missing 1 required positional argument: ‘self‘ 33 34 def f1(): 35 print("f1") 36 37 p1.f1 = f1 # 這時方法是保存在對象中的 38 39 p1.f1() 40 41 Person.f1() 42 # ==>AttributeError: type object ‘Person‘ has no attribute ‘f1‘
相同點:對於所有的方法而言,均屬於類(非對象)中,所以,在內存中也只保存一份。
不同點:方法調用者不同、調用方法時自動傳入的參數不同。3.屬性
a.屬性是什麽? 屬性存在意義是:訪問屬性時可以制造出和訪問字段完全相同的假象 屬性由方法變種而來,如果Python中沒有屬性,方法完全可以代替其功能。 屬性的功能是:屬性內部進行一系列的邏輯計算,最終將計算結果返回。b.屬性的基本使用
1 class Dog(object): 2 """ 這個類是描述狗這個對象的 """ 3 4 def __init__(self, name): 5 self.name = name 6 self.__food = "包子" 7 8 @property # attribute 9 def eat(self): 10 print("%s is eating %s" % (self.name, self.__food)) 11 return self.__food 12 13 d = Dog("flydog") 14 d.eat
c.屬性的兩種定義方式
屬性的定義有兩種方式:
- 裝飾器 即:在方法上應用裝飾器
- 靜態字段 即:在類中定義值為property對象的靜態字段
1 class Dog(object): 2 """ 這個類是描述狗這個對象的 """ 3 4 def __init__(self, name): 5 self.name = name 6 self.__food = None 7 8 @property # attribute 9 def eat(self): 10 print("%s is eating %s" % (self.name, self.__food)) 11 return self.__food 12 13 @eat.setter 14 def eat(self, food): 15 print("set to food:", food) 16 self.__food = food 17 18 @eat.deleter 19 def eat(self): 20 del self.__food 21 print("刪完了") 22 23 def talk(self): 24 print("%s is talking" % self.name) 25 26 d = Dog("flydog") 27 print(d) 28 d.eat = "包子" 29 d.eat 30 del d.eat
靜態字段方式,創建值為property對象的靜態字段
1 class Dog(object): 2 """ 這個類是描述狗這個對象的 """ 3 4 def __init__(self, name): 5 self.name = name 6 self.__food = None 7 8 def get_food(self): 9 print("%s is eating %s" % (self.name, self.__food)) 10 return self.__food 11 12 def set_food(self, food): 13 print("set to food:", food) 14 self.__food = food 15 16 def del_food(self): 17 del self.__food 18 print("刪完了") 19 20 def talk(self): 21 print("%s is talking" % self.name) 22 23 FOOD = property(get_food, set_food, del_food, "食物屬性描述") 24 25 d = Dog("flydog") 26 d.FOOD = "包子" 27 d.FOOD 28 del d.FOOD
property的構造方法中有個四個參數
- 第一個參數是方法名,調用
對象.屬性
時自動觸發執行方法 - 第二個參數是方法名,調用
對象.屬性 = XXX
時自動觸發執行方法 - 第三個參數是方法名,調用
del 對象.屬性
時自動觸發執行方法 - 第四個參數是字符串,調用
對象.屬性.__doc__
,此參數是該屬性的描述信息
二、類成員修飾符
類的所有成員在上一步驟中已經做了詳細的介紹,對於每一個類的成員而言都有兩種形式:
- 公有成員,在任何地方都能訪問
- 私有成員,只有在類的內部才能方法
私有成員和公有成員的定義不同:私有成員命名時,前兩個字符是下劃線。(特殊成員除外,例如:__init__、__call__、__dict__等)
1 class Student(object): 2 3 def __init__(self, name, age, score): 4 self.name = name # 公有字段 5 self.__age = age # 私有字段 6 self.__score = score # 私有字段 7 8 def print_score(self): 9 print(‘%s: %s‘ % (self.name, self.__score))有些時候,你會看到以一個下劃線開頭的實例變量名,比如
__score
,這樣的實例變量外部是可以訪問的,但是,按照約定俗成的規定,當你看到這樣的變量時,意思就是,“雖然我可以被訪問,但是,請把我視為私有變量,不要隨意訪問”。
雙下劃線開頭的實例變量是不是一定不能從外部訪問呢?其實也不是。不能直接訪問是__score
因為Python解釋器對外把變量__score改成了 _Student__score
,所以,仍然可以通過_Student__score
來訪問__socre
變量:
但是強烈建議你不要這麽幹,因為不同版本的Python解釋器可能會把__score
改成不同的變量名。
最後註意下面的這種錯誤寫法:
1 >>> bart = Student(‘Bart Simpson‘, 25, 98) 2 >>> bart.print_score() 3 Bart Simpson: 98 4 >>> bart.__score = 100 # 設置__score變量! 5 >>> bart.__score 6 100表面上看,外部代碼“成功”地設置了
__score
變量,但實際上這個變量和__score
class內部的__score
變量不是一個變量!內部的__score
變量已經被Python解釋器自動改成了_Student__score
,而外部代碼給bart
新增了一個__score
變量。不信試試:
1 >>> bart.print_score() 2 Bart Simpson: 98
私有成員和公有成員的訪問限制不同:
- 公有成員:類可以訪問;類內部可以訪問;派生類中可以訪問
- 私有成員 :僅類內部可以訪問;
1 class A(object): 2 a = "a" 3 __b = "b" 4 5 def __init__(self, name, sex): 6 self.name = name 7 self.__sex = sex 8 9 def get_a(self): 10 print(self.a) 11 12 def get_b(self): 13 print(self.__b) 14 15 class B(A): 16 17 def show_name(self): 18 print(self.name) 19 20 def show_sex(self): 21 print(self.__sex) 22 23 obj1 = A("Dog", "F") 24 obj2 = B("Wolf", "M") 25 print(obj1.a) # 對象可以訪問公有靜態字段 26 # print(obj1.__b) # 對象不可以訪問私有靜態字段 27 obj1.get_a() # 類內部可以訪問公有靜態字段 28 obj1.get_b() # 類內部可以訪問私有靜態字段 29 obj2.get_b() # 子類可以訪問父類私有靜態字段 30 obj2.show_name() # 子類可以訪問父類的公有字段 31 print(obj2.a) # 子類可以訪問父類的公有靜態字段 32 # print(obj2.__b) # 子類不可以訪問私有靜態字段 33 print(obj2.name) # 子類可以訪問公有字段 34 # obj2.show_sex() # 子類不能訪問父類的私有字段
三、類的特殊成員
一、類的特殊成員是什麽? 前面了解了在成員名前面加兩個下劃線,則可以把該成員變成私有成員,私有成員規定只能由類內部來調用。 那麽成員名前後都有兩個下劃線呢?這就是類的特殊成員了,所以我們在命名一些成員時,不要在前後加上兩個 下劃線。
二、具體有哪些特殊成員呢? 1. __doc__ 表示類的描述信息
1 class Dog(object): 2 """ 這個類是描述狗這個對象的 """ 3 4 def __init__(self, name): 5 self.name = name 6 self.__food = None 7 d = Dog("flydog") 8 print(d.__doc__) 9 # ==> 這個類是描述狗這個對象的2. __module__ 和 __class__ __module__ 表示當前操作的對象在哪個模塊 __class__ 表示當前操作的對象的類是什麽
1 class C: 2 3 def __init__(self): 4 self.name = ‘Breakering‘lib/aa
1 from lib.aa import C 2 3 obj = C() 4 print obj.__module__ # 輸出 lib.aa,即:輸出模塊 5 print obj.__class__ # 輸出 lib.aa.C,即:輸出類test 3. __init__ 構造方法,通過類創建對象時,自動觸發執行。
1 class C: 2 3 def __init__(self): 4 self.name = ‘Breakering‘4. __del__ 析構方法,當對象在內存中被釋放時,自動觸發執行。 註:此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。但是可以在對象銷毀前進行一些相應的操作。
1 class Foo: 2 3 def __del__(self): 4 # 關閉服務器 5 # 關閉進程 6 ...5. __call__ 對象後面加括號,觸發執行。 註:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
1 class Foo: 2 3 def __init__(self): 4 pass 5 6 def __call__(self, *args, **kwargs): 7 8 print ‘__call__‘ 9 10 11 obj = Foo() # 執行 __init__ 12 obj() # 執行 __call__6. __dict__ 類或對象中的所有成員
1 class Person(object): 2 3 _planet = "earth" 4 5 def __init__(self, name): 6 self.name = name 7 8 def talk(self): # 普通方法 9 print("%s is say Hello!" % self.name) 10 11 @classmethod 12 def get_planet(cls): # 類方法 13 return cls._planet 14 15 @staticmethod 16 def test(): 17 print("test") 18 19 print(Person.__dict__) 20 # {‘talk‘: <function Person.talk at 0x0000020E50C23510>, ‘__init__‘: <function Person.__init__ at 0x0000020E50C23488>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘Person‘ objects>, ‘__module__‘: ‘__main__‘, ‘__doc__‘: None, ‘test‘: <staticmethod object at 0x0000020E50C2C160>, ‘_planet‘: ‘earth‘, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Person‘ objects>, ‘get_planet‘: <classmethod object at 0x0000020E50C2C128>} 21 22 print(p1.__dict__) 23 # {‘name‘: ‘Breakering‘}7. __str__ 如果一個類中定義了__str__方法,那麽在打印 對象 時,默認輸出該方法的返回值。
1 class Foo: 2 3 def __str__(self): 4 return ‘Breakering‘ 5 6 7 obj = Foo() 8 print(obj) 9 # 輸出:Breakering
但是細心的朋友會發現直接敲變量不用print
,打印出來的實例還是不好看:
1 >>> obj 2 <__main__.Foo object at 0x109afb310>這是因為直接顯示變量調用的不是
__str__()
,而是__repr__()
,兩者的區別是__str__()
返回用戶看到的字符串,而__repr__()
返回程序開發者看到的字符串,也就是說,__repr__()
是為調試服務的。
解決辦法是再定義一個__repr__()
。但是通常__str__()
和__repr__()
代碼都是一樣的,所以,有個偷懶的寫法:
1 class Foo: 2 3 def __str__(self): 4 return ‘Breakering‘ 5 _repr__ = __str__8、__getitem__、__setitem__、__delitem__ 用於索引操作,如字典。以上分別表示獲取、設置、刪除數據
1 class Foo(object): 2 3 def __getitem__(self, key): 4 print ‘__getitem__‘,key 5 6 def __setitem__(self, key, value): 7 print ‘__setitem__‘,key,value 8 9 def __delitem__(self, key): 10 print ‘__delitem__‘,key 11 12 13 obj = Foo() 14 15 result = obj[‘k1‘] # 自動觸發執行 __getitem__ 16 obj[‘k2‘] = ‘wupeiqi‘ # 自動觸發執行 __setitem__ 17 del obj[‘k1‘] # 自動觸發執行 __delitem__View Code 9、__getslice__、__setslice__、__delslice__ 該三個方法用於分片操作,如:列表
1 class Foo(object): 2 3 def __getslice__(self, i, j): 4 print ‘__getslice__‘,i,j 5 6 def __setslice__(self, i, j, sequence): 7 print ‘__setslice__‘,i,j 8 9 def __delslice__(self, i, j): 10 print ‘__delslice__‘,i,j 11 12 obj = Foo() 13 14 obj[-1:1] # 自動觸發執行 __getslice__ 15 obj[0:1] = [11,22,33,44] # 自動觸發執行 __setslice__ 16 del obj[0:2] # 自動觸發執行 __delslice__View Code 10. __iter__ 用於叠代器,之所以列表、字典、元組可以進行for循環,是因為類型內部定義了 __iter_
1 class Foo(object): 2 3 def __init__(self, sq): 4 self.sq = sq 5 6 def __iter__(self): 7 return iter(self.sq) 8 9 obj = Foo([11,22,33,44]) 10 11 for i in obj: 12 print i
11. __new__ 和 __metaclass__
1 class Foo(object): 2 3 def __init__(self): 4 pass 5 6 obj = Foo() # obj是通過Foo類實例化的對象
1 print(type(obj)) # 輸出:<class ‘__main__.Foo‘> 表示,obj 對象由Foo類創建 2 print(type(Foo)) # 輸出:<type ‘type‘> 表示,Foo類對象由 type 類創建
obj對象是Foo類的一個實例,Foo類對象是 type 類的一個實例, 即:Foo類對象 是通過type類的構造方法創建。
那麽,創建類就可以有兩種方式:
a). 普通方式
1 class Foo(object): 2 3 def func(self): 4 print(‘hello‘)
b).特殊方式(type類的構造函數)
1 def func(self): 2 print(‘hello‘) 3 4 Foo = type(‘Foo‘,(object,), {‘func‘: func}) 5 #type第一個參數:類名 6 #type第二個參數:當前類的基類 7 #type第三個參數:類的成員
註:類是由type類實例化產生的
那麽問題來了,類默認是由 type 類實例化產生,type類中如何實現的創建類?類又是如何創建對象?
答:類中有一個屬性 __metaclass__,其用來表示該類由 誰 來實例化創建,所以,我們可以為 __metaclass__ 設置一個type類的派生類,從而查看 類 創建的過程。
Python Day7