標籤:讀書筆記 python
下面開始是進階部分了。
四、類
1. 類
在Python2.2之前使用的是舊式類,2.2版本之後使用的是新式類,但是在Python3之後就不存在這個問題了。下面談論的問題是關於新式類的。
關於新式類的定義有兩種方式。
1)繼承性定義。
<span style="font-size:18px;">class A(object):passa = A()print a.__class__#<class '__main__.A'>print type(a)#<class '__main__.A'></span>
2)在類的前面寫上這麼一句:__metaclass__ == type(表示下面定義的類是新式類),然後定義類的時候,就不需要在名字後面寫(object)了。
<span style="font-size:18px;">__metaclass__ = typeclass A:passa = A()print a.__class__#<class '__main__.A'>print type(a)#<class '__main__.A'></span>
下面介紹了建立一個新的類。類中的方法的參數必須包括self參數,並且作為預設的第一個參數。def __init__叫做初始化函數。
<span style="font-size:18px;">__metaclass__ = typeclass Person:def __init__(self,name):self.name = namedef getName(self):return self.namedef age(self,age):print "%s is %d years old" % (self.name,age)p = Person('why')print p.getName()#whyp.age(22.5)#why is 22 years old</span>
當類中變數引用的是不可變資料時,執行個體屬性不會影響類屬性,而類屬性會影響執行個體屬性。當類中變數引用的是可變對象時,類屬性和執行個體屬性都能直接修改這個對象,從而影響另一方的值。
<span style="font-size:18px;">class A(object):x = 3y = [1,2,3]a = A()a.x = 4print A.x#3print a.x#4a.y.append(4)print A.y#[1, 2, 3, 4]print a.y#[1, 2, 3, 4]</span>
在類確定或者執行個體化之後,也可以增加和修改屬性,其方法就是通過類或者執行個體的點號操作來實現,即object.attribute,可以實現對屬性的修改和增加。但是增加執行個體屬性,類屬性不會變。
<span style="font-size:18px;">class A(object):x = 3y = [1,2,3]a = A()a.z = 'why'print a.z#whyprint A.z#AttributeError: type object 'A' has no attribute 'z'A.q = 22.5print a.q#22.5print A.q#22.5</span>
命名空間因為對象的不同,也有所區別,可以分為如下幾種:
1)內建命名空間(Built-in Namespaces):Python運行起來,它們就存在了。內建函數的命名空間都屬於內建命名空間,所以,我們可以在任何程式中直接運行它們,比如d(),不需要做什麼操作,拿過來就直接使用了。
2)全域命名空間(Module:Global Namespaces):每個模組建立它自己所擁有的全域命名空間,不同模組的全域命名空間彼此獨立,不同模組中相同名稱的命名空間,也會因為模組的不同而不相互幹擾。
3)本地命名空間(Function&Class: Local Namespaces):模組中有函數或者類,每個函數或者類所定義的命名空間就是本地命名空間。如果函數返回了結果或者拋出異常,則本地命名空間也結束了。
此外,還談到了資料輪轉和範圍的問題。
下面是談了類的繼承和方法的重寫,由於和JAVA差不多,所以不再贅述。
在舊式類中多重繼承的順訊是依照深度優先來的,而在新式類中是按照廣度優先來的。類的__mro__屬性可以得到類的繼承順序。
<span style="font-size:18px;">class A(object):</span>
<span style="font-size:18px;">def foo(self):print "foo A"class B(object):def foo(self):print "foo B"def bar(self):print "bar B"class C(A,B):passclass D(A,B):def bar(self):print "bar D"class E(C,D):passprint E.__mro__ #(<class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>)e = E()e.foo()#foo Ae.bar()#bar D</span>
在新式類中如果要調用父類的方法,可使用super函數,尤其是在初始化函數的時候。super函數的參數,第一個是當前子類的類名字,第二個是self,然後是點號,點號後面是所要調用的父類的方法。
<span style="font-size:18px;">__metaclass__ = typeclass Person:def __init__(self):self.name = 'why'def p(self):print "My name is {}".format(self.name)class Employer(Person):def __init__(self):super(Employer,self).__init__()self.age = 22.5def p(self):print "I am {} years old".format(self.age)super(Employer,self).p()e = Employer()e.p()# I am 22.5 years old# My name is why</span>
在python中:@staticmethod表示下面的方法是靜態方法,@classmethod表示下面的方法是類方法。靜態方法依然用def語句來定義。需要注意的是檔案名稱後面的括弧內沒有self,那麼也就無法訪問執行個體變數、類和執行個體的屬性了,因為它們都是藉助self來傳遞資料的。類方法的參數也沒有self,但是必須有cls這個參數。在類方法中,能夠訪問類屬性,但是不能訪問執行個體屬性。兩種方法都可以通過執行個體調用,即綁定執行個體。也可以通過類來調用。關於兩者的差異看以參見這篇文章PYTHON中STATICMETHOD和CLASSMETHOD的差異。
在函數、類或者檔案開頭的部分寫文檔字串說明,一般採用三重引號。這樣寫的最大好處是能夠用help()函數看。
<span style="font-size:18px;">"""This is python lesson"""def start_func(arg): """This is a function.""" passclass MyClass: """This is my class.""" def my_method(self,arg): """This is my method.""" pass</span>
2. 多態和封裝
count()函數的作用是數一數某個元素在對象中出現的次數。
<span style="font-size:18px;">print "wwhhy".count('w')#2print [1,1,2,3,4].count(2)#1</span> repr()函數能夠針對輸入的任何對象返回一個字串。用它可以體現出多態。
<span style="font-size:18px;"><span style="font-size:18px;">def length(x):print "the length of " , repr(x) , "is ", len(x)length([1,2,3])#the length of [1, 2, 3] is 3length('why')#the length of 'why' is 3length({1:'why',2:'zmq'})#the length of {1: 'why', 2: 'zmq'} is 2</span></span> python中私人化的方法也比較簡單,就是在準備私人化的屬性(包括方法、資料)名字前面加雙底線。如果要調用那些私人屬性可以使用property函數。用了@property之後,在調用那個方法的時候,用的是p.xx的形式,就好像在調用一個屬性一樣,
<span style="font-size:18px;">__metaclass__ = typeclass A: def __init__(self): self.me = "why" self.__name = "zmq" @property def name(self): return self.__nameif __name__ == "__main__": p = A() print p.name#zmq</span>
3. 特殊方法
接下來介紹了幾個特殊方法:
1)__dict__:儲存了對象的屬性。下面是一個綜合的例子,屬性和方法的情況都類似。
<span style="font-size:18px;">class A(object):name = 'why'print A.__dict__#{'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, 'name': 'why', '__doc__': None}print A.__dict__['name']#whyprint A.name#whya = A()print a.__dict__#{}print a.name#whya.name = 'zmq'print a.__dict__#{'name': 'zmq'}print a.__dict__['name']#zmqprint a.name#zmqprint A.__dict__#{'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, 'name': 'why', '__doc__': None}print A.__dict__['name']#whyprint A.name#whydel a.nameprint a.__dict__#{}print a.name#whya.age = 22.5print a.__dict__#{'age': 22.5}print a.age#22.5print A.__dict__['age']#KeyError: 'age'print A.age#AttributeError: type object 'A' has no attribute 'age'A.major = 'computer'print a.__dict__#{'age': 22.5}print a.major#computer</span> 2)_slots__:能夠限制屬性的定義,在編程中非常重要的方面是最佳化記憶體使用量。_slots__已經把執行個體屬性牢牢地管控了起來,但更本質是的是最佳化了記憶體。這種最佳化會在大量的執行個體時候顯出效果。
<span style="font-size:18px;">class A(object):__slots__ = ('name','age')print A.__slots__#('name', 'age')a = A()print a.__slots__#('name', 'age')A.name = 'why'print a.name#why# a.name = 'zmq'#AttributeError: 'A' object attribute 'name' is read-onlya.age = 22.5print a.age#22.5print A.age#<member 'age' of 'A' objects>A.age = 23print a.age#23a.major = 'computer'#AttributeError: 'A' object has no attribute 'major'</span> 3)__setattr__(self,name,value):如果要給name賦值,就調用這個方法。
4)__getattr__(self,name):如果name被訪問,同時它不存在的時候,此方法被調用。
<span style="font-size:18px;">class A(object):def __getattr__(self, name):print "You use getattr"def __setattr__(self, name, value):print "You use setattr"self.__dict__[name] = valuea = A()a.x#You use getattra.x = 3#You use setattrprint a.x#3</span>
5)__getattribute__(self,name):當name被訪問時自動被調用(注意:這個僅能用於新式類),無論name是否存在,都要被調用。
6)__delattr__(self,name):如果要刪除name,這個方法就被調用。
<span style="font-size:18px;">class B(object):def __getattribute__(self, name):print "you are useing getattribute"return object.__getattribute__(self, name)b = B()b.y#you are useing getattribute AttributeError: 'B' object has no attribute 'y'b.y = 4print b.y#4</span>
通過執行個體擷取其屬性,如果在__dict__中有相應的屬性,就直接返回其結果;如果沒有,會到類屬性中找。如果沒有定義__getattr__()方法,就會引發AttributeError。
<span style="font-size:18px;">__metaclass = typeclass A:name = 'why'def __getattr__(self,key):if key != 'name':return "check again"a = A()print a.name#whyprint a.major#check again</span>
4. 迭代器
__iter__就是對象的一個特殊方法,它是迭代規則的基礎。對象如果沒有它,就不能返回迭代器,就沒有next()方法,就不能迭代。可以讓自己寫的對象能夠迭代。
<span style="font-size:18px;">__metaclass__ = typeclass A:def __init__(self,n):self.i = 0self.n = ndef __iter__(self):return selfdef next(self):if self.i < self.n:i = self.iself.i += 1return ielse:raise StopIteration()if __name__ == "__main__":a = A(7)print a.next()print a.next()print "------------"for i in a:print i# 0# 1# ------------# 2# 3# 4# 5# 6</span>
關於列表和迭代器之間的區別,還有兩個非常典型的內建函數:range()和xrange()。range()返回的是一個列表,xrange()返回的是一個對象,在迴圈的時候稍快並有更高的記憶體效率。即通過range()得到的列表,會一次性被讀入記憶體,而xrange()返回的對象,則是需要一個數值才從返回一個數值。下面的例子將會體現xrange()函數的優勢。
<span style="font-size:18px;">__metaclass__ = typea = range(4)b = xrange(10000)print zip(a,b)3[(0, 0), (1, 1), (2, 2), (3, 3)]</span>
5. 產生器
產生器必須是可迭代的,它比列表有更好的執行效率。表示形式是將列表解析的 { } 其換成 ( ) 。
<span style="font-size:18px;"><span style="font-size:18px;">a = (i**2 for i in range(4))for i in a:print i,#0 1 4 9print ""for i in a:print i,#</span></span>
yeild關鍵詞是產生器的標識。函數返回值是一個產生器類型的對象,這個產生器對象就是迭代器。我們把含有yield語句的函數稱作產生器。產生器是一種用普通函數文法定義的迭代器。
<span style="font-size:18px;">def a():yield 0yield 1yield 2a = a()print a.next()#0print a.next()#1print a.next()#2print a.next()#StopIteration</span>
發現yield除了作為產生器的標誌之外,還有一個功能就是返回值。return會結束函數體的執行,而yeild則會暫時掛起,等到下次調用next方法時再繼續。
<span style="font-size:18px;">def a():print "before"for i in range(4):return iprint "after"aa = a()#beforeprint aa#0print aa#0def b():print "before"for i in range(4):yield iprint "after"bb = b()#beforeprint bb.next()#0print bb.next()#after 1</span>
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
《零基礎學Python(第二版)》(四)