《零基礎學Python(第二版)》(四)

來源:互聯網
上載者:User

標籤:讀書筆記   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(第二版)》(四)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.