Python的語言特性


 

1、Python的函數傳參

  Python中所有的變量都可以理解為內存中一個對象的“引用”,或者,也可以看似C中的void *的感覺。這里記住的是類型是屬於對象的,而不是變量。對象分為兩種:

  可更改的:list,dict;

  不可更改的:strings,tuples,numbers;

  當向函數傳遞一個參數,即引用的時候:

   1)如果該參數是函數外一個不可變的對象的引用,則函數執行完之后,在函數外打印的是原來的值,與函數對該引用做什么事情無關。

   2)如果該函數是函數外一個可變的對象的引用,則函數執行完之后,在函數外打印的是該函數多該引用所執行的內存空間改變的結果。

 

2、普通方法、實例方法、類方法和靜態方法

  普通方法:在一個Python環境中,獨立於類或者對象的函數,可直接導入文件中的方法直接使用。

  實例方法:實例定義來類中,使用之前先通過類實例化一個對象,然后在通過對象調用實例方法。與c++類似。實例方法的第一個參數默認傳入一個self,這個self會與實例綁定。

  類方法:與實例方法的調用方式類似,不同的是類方法第一個參數默認傳入一個類cls,而不是對象。

  靜態方法:對於靜態方法其實和普通的方法一樣,不需要對誰進行綁定,唯一的區別是調用的時候需要使用a.static_foo(x)或者A.static_foo(x)來調用.

 

  

  靜態方法修飾:@staticmethod

  類方法修飾:@classmethod

 1 #普通方法
 2 def foo(x):
 3     pass
 4 
 5 class A(object):
 6     #實例方法
 7     def foo(self,x):
 8         pass
 9     
10     #類方法
11     @classmethod
12     def class_foo(cls,x):
13         pass
14     
15     #靜態方法
16     @staticmethod
17     def static_foo(x):
18         pass

 

3、類屬性和實例屬性

  類屬性就是供類使用的屬性,實例屬性就是供實例使用的屬性。

  由於Python是動態語言,根據類創建的實例可以任意綁定屬性。

  給實例綁定屬性的方法是通過實例變量,或者通過self變量:

1 class Student(object):
2     def __init__(self, name):
3         self.name = name
4 
5 s = Student('Bob')
6 s.score = 90

  但是,如果Student類本身需要綁定一個屬性呢?可以直接在class中定義屬性,這種屬性是類屬性,歸Student類所有

1 class Student(object):
2     name = 'Student'

  當我們定義了一個類屬性后,這個屬性雖然歸類所有,但類的所有實例都可以訪問到。來測試一下:

>>> class Student(object):
...     name = 'Student'
...
>>> s = Student() # 創建實例s
>>> print(s.name) # 打印name屬性,因為實例並沒有name屬性,所以會繼續查找class的name屬性
Student
>>> print(Student.name) # 打印類的name屬性
Student
>>> s.name = 'Michael' # 給實例綁定name屬性
>>> print(s.name) # 由於實例屬性優先級比類屬性高,因此,它會屏蔽掉類的name屬性
Michael
>>> print(Student.name) # 但是類屬性並未消失,用Student.name仍然可以訪問
Student
>>> del s.name # 如果刪除實例的name屬性
>>> print(s.name) # 再次調用s.name,由於實例的name屬性沒有找到,類的name屬性就顯示出來了
Student

  從上面的例子可以看出,在編寫程序的時候,千萬不要對實例屬性和類屬性使用相同的名字,因為相同名稱的實例屬性將屏蔽掉類屬性,但是當你刪除實例屬性后,再使用相同的名稱,訪問到的將是類屬性

 

4、Python自省

  自省就是面向對象的語言所寫的程序在運行時,所能知道對象的類型.簡單一句就是運行時能夠獲得對象的類型.比如type(),dir(),getattr(),hasattr(),isinstance()

 

5、python的各種推導式(列表推導式、字典推導式、集合推導式) 

  推導式comprehensions(又稱解析式),是Python的一種獨有特性。推導式是可以從一個數據序列構建另一個新的數據序列的結構體。 共有三種推導,在Python2和3中都有支持:

  • 列表(list)推導式
  • 字典(dict)推導式
  • 集合(set)推導式

  使用[]生成list

  基本格式

variable = [out_exp_res for out_exp in input_list if out_exp == 2]
  out_exp_res:  列表生成元素表達式,可以是有返回值的函數。
  for out_exp in input_list:  迭代input_list將out_exp傳入out_exp_res表達式中。
  if out_exp == 2:  根據條件過濾哪些值可以。

注意:表達式是用方括號擴起來,說明生成的是一個列表。

實例一:
multiples = [i for i in range(30) if i % 3 is 0]
print(multiples)
# Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
實例二:

def squared(x):
    return x*x
multiples = [squared(i) for i in range(30) if i % 3 is 0]
print multiples
#  Output: [0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

  使用()生成generator

  將列表推導式的 [] 改成 () 即可得到生成器。以下會介紹生成器。

multiples = (i for i in range(30) if i % 3 is 0)
print(type(multiples))
#  Output: <type 'generator'>

  

  字典推導式

   字典推導和列表推導的使用方法是類似的,只不中括號該改成大括號。

  基本格式

variable = {out_exp_res for out_exp in input_list if out_exp == 2}
  out_exp_res:  列表生成元素表達式,可以是有返回值的函數。
  for out_exp in input_list:  可迭代的input_list將out_exp傳入out_exp_res表達式中。
  if out_exp == 2:  根據條件過濾哪些值可以。

例子一:大小寫key合並

mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
mcase_frequency = {
    k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0)
    for k in mcase.keys()
    if k.lower() in ['a','b']
}
print mcase_frequency
#  Output: {'a': 17, 'b': 34}

例子二:快速更換key和value

mcase = {'a': 10, 'b': 34}
mcase_frequency = {v: k for k, v in mcase.items()}
print mcase_frequency
#  Output: {10: 'a', 34: 'b'}

 

  集合推導式

  它們跟列表推導式也是類似的。 唯一的區別在於它使用大括號{}。

squared = {x**2 for x in [1, 1, 2]}
print(squared)
# Output: set([1, 4])

 

6、生成器generator

  通過列表生成式,我們可以直接創建一個列表。但是,受到內存限制,列表容量肯定是有限的。而且,創建一個包含100萬個元素的列表,不僅占用很大的存儲空間,如果我們僅僅需要訪問前面幾個元素,那后面絕大多數元素占用的空間都白白浪費了。

  所以,如果列表元素可以按照某種算法推算出來,那我們是否可以在循環的過程中不斷推算出后續的元素呢?這樣就不必創建完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱為生成器:generator。

  要創建一個generator,有很多種方法。第一種方法很簡單,只要把一個列表生成式的[]改成(),就創建了一個generator:在第五點中有陳述

  如果要一個一個打印出來,可以通過next()函數獲得generator的下一個返回值

>>> next(g)
0
>>> next(g)
1
>>> next(g)
4
>>> next(g)
9
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

  不斷調用next(g)實在是太變態了,正確的方法是使用for循環,因為generator也是可迭代對象

>>> g = (x * x for x in range(10))
>>> for n in g:
...     print(n)
... 
0
1
4
9

 第二種generator的獲取方法:

  比如,著名的斐波拉契數列(Fibonacci),除第一個和第二個數外,任意一個數都可由前兩個數相加得到:

  1, 1, 2, 3, 5, 8, 13, 21, 34, ...

  斐波拉契數列用列表生成式寫不出來,但是,用函數把它打印出來卻很容易:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        print(b)
        a, b = b, a + b
        n = n + 1
    return 'done'

  仔細觀察,可以看出,fib函數實際上是定義了斐波拉契數列的推算規則,可以從第一個元素開始,推算出后續任意的元素,這種邏輯其實非常類似generator。

 也就是說,上面的函數和generator僅一步之遙。要把fib函數變成generator,只需要把print(b)改為yield b就可以了:

def fib(max):
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
    return 'done'

  如果一個函數定義中包含yield關鍵字,那么這個函數就不再是一個普通函數,而是一個generator。

  這里,最難理解的就是generator和函數的執行流程不一樣。函數是順序執行,遇到return語句或者最后一行函數語句就返回。而變成generator的函數,在每次調用next()的時候執行,遇到yield語句返回,再次執行時從上次返回的yield語句處繼續執行。

  同樣的,把函數改成generator后,我們基本上從來不會用next()來獲取下一個返回值,而是直接使用for循環來迭代:

  >>> for n in fib(6):   ... print(n)

 但是用for循環調用generator時,發現拿不到generator的return語句的返回值。如果想要拿到返回值,必須捕獲StopIteration錯誤,返回值包含在StopIterationvalue中:
 
  >>> g = fib(6)   >>> while True:   ... try:   ... x = next(g)   ... print('g:', x)   ... except StopIteration as e:   ... print('Generator return value:', e.value)   ... break   ...   g: 1   g: 1   g: 2   g: 3   g: 5   g: 8   Generator return value: done

 

7、Python中單下划線和雙下划線  

  __foo__:一種約定,Python內部的名字,用來區別其他用戶自定義的命名,以防沖突.

  _foo:一種約定,用來指定變量私有.程序員用來指定私有變量的一種方式.

  __foo:這個有真正的意義:解析器用_classname__foo來代替這個名字,以區別和其他類相同的命名.

 

8、字符串格式化:%和format函數格式化的用法

 .format在許多方面看起來更便利.對於%最煩人的是它無法同時傳遞一個變量和元組.你可能會想下面的代碼不會有什么問題:

 

 但是,如果name恰好是(1,2,3),它將會拋出一個TypeError異常.為了保證它總是正確的,你必須這樣做:

 

 但是有點丑..format就沒有這些問題.你給的第二個問題也是這樣,.format好看多了.

  

  自python2.6開始,新增了一種格式化字符串的函數str.format(),可謂威力十足。那么,他跟之前的%型格式化字符串相比,有什么優越的存在呢?讓我們來揭開它羞答答的面紗。
 詳見如下文章:http://www.jb51.net/article/63672.htm

 

9、迭代器和生成器

   看廖雪峰的Python教程:

   迭代器:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143178254193589df9c612d2449618ea460e7a672a366000

    凡是可作用於for循環的對象都是Iterable類型;

 

    凡是可作用於next()函數的對象都是Iterator類型,它們表示一個惰性計算的序列;

 

    集合數據類型如listdictstr等是Iterable但不是Iterator,不過可以通過iter()函數獲得一個Iterator對象。

 

    Python的for循環本質上就是通過不斷調用next()函數實現的,例如:

 

   生成器:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000  

    generator是非常強大的工具,在Python中,可以簡單地把列表生成式改成generator,也可以通過函數實現復雜邏輯的generator。

    要理解generator的工作原理,它是在for循環的過程中不斷計算出下一個元素,並在適當的條件結束for循環。對於函數改成的generator來說,遇到return語句或者執行到函數體最后一行語句,就是結束generator的指令,for循環隨之結束。

    請注意區分普通函數和generator函數,普通函數調用直接返回結果:

    >>> r = abs(6)
    >>> r
    6

    generator函數的“調用”實際返回一個generator對象:

    >>> g = fib(6)
    >>> g
    <generator object fib at 0x1022ef948>

 

10、*args and **kwargs(可變參數)

  用*args**kwargs只是為了方便並沒有強制使用它們.

  當你不確定你的函數里將要傳遞多少參數時你可以用*args.例如,它可以傳遞任意數量的參數:

def print_everything(*args):
    for count,thing int enumerate(args):
        print '{0},{1}'.format(count,thing);
print_everything('apple','banana','cabbage');

output:
0,apple
1,banana
2,cabbage

 

  相似的,**kwargs允許你使用沒有事先定義的參數名:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM