Python的易混地帶

每門編程語言都有易混淆的部分,下面列舉出一些Python的易混淆知識點。

  1. == 和 is 的區別
    在Python中,”==” 操作符測試值的相等性; “is”表達式測試對象的一致性,即是否指向同一個對象。
    list1 = [1,('a',3)]
    list2 = [1,('a',3)]
    list1 == list2, list1 is list2   #(True,False)

    說明:list1和list2通過了”==”測試 (他們的值相等,因爲它們的所有內容都是相等的),但是is測試卻失敗了(它們是兩個不同的對象,因此有不同的內存區域

    s1="spam"
    s2="spam"
    s1 == s2,s1 is s2    #(True,True)

    說明:或許你會驚訝,覺得這組得到的結果應該和上組得到的結果一致。事實上,內存中只有一個字符串’spam’供s1和s2共享。這個主要是因爲在 Python內部會暫時存儲並重復使用短字符串。也就是說當創建短字符串的時候會首先到字符串的內存區域查找是否已經有該字符串相等的值存在,如果有則會指向該內存區域,否則重新開闢內存。

    s1='a b'
    s2='a b'
    s1 == s2,s1 is s2    #(True,False)

    說明:驚訝再次產生,你或許會質疑這不和上組的一樣嘛,爲啥結果不一樣了? Confused 其實上組的說明部分已經說了,Python只是暫時存儲短字符串,像這樣中間有空格的字符串和較長的字符串,Python是不會存儲的。也就是說,像這樣的字符串創建時,Python會直接開闢內存

  2. Python中的作用域
    Python 中,一個變量的作用域總是由在代碼中被賦值的地方所決定的。
    • 函數定義了本地作用域,而模塊定義的是全局作用域。
      如果想要在函數內定義全局作用域,需要加上global修飾符。
    • 變量名解析:LEGB原則
      當在函數中使用未認證的變量名時,Python搜索4個作用域[本地作用域(L),之後是上一層結構中def或者lambda的本地作用域(E),
      之後是全局作用域(G),最後是內置作用域(B)]並且在第一處能夠找到這個變量名的地方停下來。如果變量名在整個的搜索過程中
      都沒有找到,Python就會報錯。
      補:上面的變量規則只適用於簡單對象,當出現引用對象的屬性時,則有另一套搜索規則:屬性引用搜索一個或多個對象,而不是作用域,並且有可能涉及到所謂的"繼承"
    • 訪問全局變量演示
      # thismod.py
      var = 99
      
      def local():
      var = 0
      
      def glob1():
      global var
      var += 1
      
      def glob2():
      var = 0
      import thismod
      thismod.var += 1
      
      def glob3():
      var = 0
      import sys
      glob = sys.modules['thismod']
      glob.var += 1
      
      def test():
      print var
      local();glob1();glob2();glob3();
      print var

      使用交互式測試該程序

      >>>import thismod
      >>>thismod.test()
      99
      102
    • 嵌套作用域演示
      def f1():
      x = 88
      def f2():
      print x
      f2()
      f1()    # 88
    • 作用域與帶有循環變量的默認參數相比較
      不指定默認值的情況
      >>>def makeActions():
      ...  acts = []
      ...  for i in range(5):
      ...    acts.append(lambda x: i ** x)
      ...  return acts
      ...
      >>>acts = makeActions()
      >>>acts[0]
      <function <lambda> at 0x7f86aaf4c758>
      >>> acts[0](2)
      16
      >>> acts[2](2)
      16
      >>> acts[4](2)
      16

      指定默認值的情況:

      >>> def makeActions():
      ...   acts = []
      ...   for i in range(5):
      ...     acts.append(lambda x, i=i: i ** x)
      ...   return acts
      ...
      >>> acts = makeActions()
      >>> acts[0](2)
      0
      >>> acts[2](2)
      4
      >>> acts[4](2)
      16

      疑答:嵌套作用域中的變量在嵌套的函數被調用時才進行查找,所以它們實際上記住的是同樣的值(在最後一次循環迭代中循環變量的值)。指定默認值可記住每一個循環變量的值。

  3. import與reload的區別
    • import 只導入一次,而reload可以在不中止Python程序的情況下,多次載入
      ---- 編寫一個模塊文件changer.py ----
      message = "first version"
      
      def printer():
      print message
      ------- the end --------
      
      >>> import changer
      >>> changer.printer()
      first version
      
      不要關掉解釋器,現在在另一個窗口編輯該模塊文件
      message = "After version"
      
      def printer():
      print 'reloaded:',message
      
      然後回到交互模式
      >>> import changer
      >>> changer.printer()
      first version
      >>> reload(changer)
      <module 'changer' from 'changer.py'>
      >>> changer.printer()
      reloaded: After version
    • reload沒有傳遞性:reload加載模塊時只重新加載該模塊,而不會加載該模塊import的其他模塊
  4. 經典類與新式類的區別
    在Python 2.2中,引入一種新的類,稱爲"新式"類,之前提到的類則稱爲"經典"類。新式類在語法和行爲上,幾乎完全和經典類兼容。他們主要的差異在於新式類從內置類型創建子類。如果沒有恰當的內置類型可用,新的內置名稱object就可以作爲新式類的超類。
    • 經典類繼承搜索程序是絕對深度優先
      經典類: 搜索順序是(D,B,A,C)
      >>> class A: attr = 1
      ...
      >>> class B(A): pass
      ...
      >>> class C(A): attr = 2
      ...
      >>> class D(B,C): pass
      ...
      >>> x = D()
      >>> x.attr
      1
    • 新式類繼承搜索程序是寬度優先
      新式類:搜索順序是(D,B,C,A)
      >>> class A(object): attr = 1
      ...
      >>> class B(A): pass
      ...
      >>> class C(A): attr = 2
      ...
      >>> class D(B,C): pass
      ...
      >>> x = D()
      >>> x.attr
      2
  5. 類變量與實例變量的區別
    當類爲self屬性賦值時,會填入實例對象。即,屬性最後會位於實例的屬性命名空間字典內,而不是類的。實例對象的命名空間保存了數據,會隨實例的不同而不同,而self正是進入其命名空間的鉤子。
    >>> class person:
    ...     def name(self, name):
    ...         self.name = name
    ...
    >>> person.__dict__
    {'__module__': '__main__', 'name': <function name at 0x7f28e96a1cf8>, '__doc__': None}
    >>> p1 = person()
    >>> p1.__dict__
    {}
    >>> p1.name('zhangsan')
    >>> p1.__dict__
    {'name': 'zhangsan'}
    >>> p2 = person()
    >>> p2.__dict__
    {}
    >>> p2.name('lisi')
    >>> p2.__dict__
    {'name': 'lisi'}
    >>> person.__dict__
    {'__module__': '__main__', 'name': <function name at 0x7f28e96a1cf8>, '__doc__': None}
  6. 靜態方法、類方法以及實例方法的區別
    在Python 2.2中,在類中定義方法是可能的,不需要實例就能夠調用它:靜態方法的運作差不多就像類中的簡單無實例函數,而類方法傳遞的是類而不是實例。名爲 staticmethod和classmethod的特定的內置函數,必須在類中調用,才能使這些方法模式有效。雖然這個功能是伴隨新式類增加的,但靜態和類方法也能用於經典類。
    定義方式,傳入的參數,調用方式都不相同。
    • staticmethod 不需要傳入self和cls對象,只有一般的參數。可以通過實例或類對象進行調用。
    • classmethod需要傳入cls對象,可以通過實例和類對象進行調用。
    • 實例method需要傳入self實例對象,可以通過實例對象調用,用類調用時需要額外傳入 實例對象。
    >>> class Multi:
    ...   def imeth(self, x):
    ...     print self, x
    ...   def smeth(x):
    ...     print x
    ...   def cmeth(cls, x):
    ...     print cls, x
    ...   smeth = staticmethod(smeth)   #make smeth a static method
    ...   cmeth = classmethod(cmeth)    #make cmeth a class method
    ...
    >>> obj = Multi()
    >>> obj.imeth(1)       #Normal call, through instance
    <__main__.Multi instance at 0x7fc537cc6170> 1
    >>> Multi.imeth(obj,2)   #Normal call, through class
    <__main__.Multi instance at 0x7fc537cc6170> 2
    >>> Multi.smeth(3)    #Static call, through class
    3
    >>> obj.smeth(4)      #Static call, through instance
    4
    >>> Multi.cmeth(5)    #Class call, through class
    __main__.Multi 5
    >>> obj.cmeth(6)      #Class call, through instance
    __main__.Multi 6

    注: Python自動把類(而不是實例)傳入類方法第一個(最左側)

    轉自http://wangsheng2008love.blog.163.com/blog/static/7820168920102464134379/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章