1. 程式人生 > >Python的基本用法

Python的基本用法

---恢復內容開始---

一、函式

1.1 預設引數

想要計算一個數x的n次方,可以定義如下的函式。但是有時候我們僅僅只需要計算x^2,所以只想使用一個引數即power(x),這時如果仍用如下程式碼會報錯

def power(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s
def power(x,n=2)

可以改成上面的形式,不傳入引數,即預設n=2。同時,當有多個預設引數時,我們可以指定為哪個預設引數賦值如下

def fun(a,b=2,c=3):
    
pass fun(4,c=6)

有一點需要注意:預設引數必須是不變物件,不能是list這樣的可變物件

def add_end(L=[]):
    L.append('END')
    return L

>>> add_end()
['END']
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']

函式定義後,預設引數的值就被計算出來了。如果預設引數是可變的,那麼每次呼叫函式後,預設引數的值就會發生改變。

1.2 可變引數

如果引數的個數是不確定的,可以使用list或者tuple進行封裝,也可以使用可變引數,方法如下:

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum

""" 使用list作為可變引數時,可以使用下面的簡單方法
>>> nums = [1, 2, 3]
>>> calc(*nums) 14

1.3 關鍵字引數

可變引數允許你傳入0個或任意個引數,這些可變引數在函式呼叫時自動組裝為一個tuple。而關鍵字引數允許你傳入0個或任意個含引數名的引數,這些關鍵字引數在函式內部自動組裝為一個dict。

def
person(name, age, **kw): print('name:', name, 'age:', age, 'other:', kw) """ 函式除了接收兩個必填引數name和age外,還可以接收若干個關鍵字引數kw >>> person('Michael', 30) name: Michael age: 30 other: {} >>> person('Bob', 35, city='Beijing') name: Bob age: 35 other: {'city': 'Beijing'} >>> person('Adam', 45, gender='M', job='Engineer') name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

1.4 命名關鍵字引數

如果要限制關鍵字引數的名字,就可以用命名關鍵字引數,例如,只接收cityjob作為關鍵字引數。這種方式定義的函式如下:

def person(name, age, *, city, job):
    print(name, age, city, job)

如果函式定義中已經有了一個可變引數,後面跟著的命名關鍵字引數就不再需要一個特殊分隔符*了:

def person(name, age, *args, city, job):
    print(name, age, args, city, job)

二  List和Tuple

2.1 List和Tuple相當於一個數組,但是數組裡可以存放的內容是不受限制的,也不需要全部相同,同時一個List裡可以有一個List。List是可變的物件,而Tuple是不可變的物件

對於List和Tuple的訪問,可以使用切片技術:

>>> L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack']

>>> L[1:3]
['Sarah', 'Tracy']

""" L[i:j]表示從索引i開始,取到索引j-1的內容

2.2 遍歷元素時,C或其他語言是按照下標進行的,而Python的遍歷可以不需要下邊,只要變數是可以迭代的即可,如dict就可以迭代

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
...     print(key)
...
a
c
b

""" 遍歷key和value

>>> for k,v in d.items():
...     print(k,v)
...
a 1
b 2
c 3

如何判斷一個物件是可迭代物件呢?方法是通過collections模組的Iterable型別判斷:

>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整數是否可迭代
False

最後一個小問題,如果要對list實現類似Java那樣的下標迴圈怎麼辦?Python內建的enumerate函式可以把一個list變成索引-元素對,這樣就可以在for迴圈中同時迭代索引和元素本身:

>>> for i, value in enumerate(['A', 'B', 'C']):
...     print(i, value)
...
0 A
1 B
2 C

2.3 列表生成式:

如果要建立一個列表,可以使用for迴圈,同時也可以有更簡單的方法進行建立

>>> list(range(1, 11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

""" for迴圈後可以增加if條件
>>> [x * x for x in range(1, 11) if x % 2 == 0] [4, 16, 36, 64, 100]

""" 可以使用兩層迴圈
>>> [m + n for m in 'ABC' for n in 'XYZ'] ['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
 

2.4  列表生成器

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

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

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

>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x1022ef630>

""" 如果要訪問生成器g的元素,可以使用for迴圈,也可以使用next(g)一個個訪問
>>> g = (x * x for x in range(10))
>>> for n in g: ... print(n) ... 0 1 4 9 16 25 36 49 64 81

生成器類似於函式,儲存的是演算法,所以可以在列印輸出時通過演算法進行計算。

定義一個計算斐波拉契數列的函式

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(6)
1
1
2 3 5 8 'done'
 

此時,只要把print(b)寫成yield b就可以把函式fib改成一個generator了

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

>>> f = fib(6)
>>> f
<generator object fib at 0x104feaaa0>

2.5 迭代器

可用next函式不斷訪問下一個元素的物件是迭代器,如generator就是一個迭代器

凡是可作用於for迴圈的物件都是Iterable型別;

凡是可作用於next()函式的物件都是Iterator型別,它們表示一個惰性計算的序列;

集合資料型別如listdictstr等是Iterable但不是Iterator,不過可以通過iter()函式獲得一個Iterator物件。

三、 函數語言程式設計

3.1 高階函式

map()函式接收兩個引數,一個是函式,一個是Iterablemap將傳入的函式依次作用到序列的每個元素,並把結果作為新的Iterator返回。

>>> def f(x):
...     return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

再看reduce的用法。reduce把一個函式作用在一個序列[x1, x2, x3, ...]上,這個函式必須接收兩個引數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

 filter()函式接收一個函式和一個序列,filter()函式將函式作用於序列上,根據作用的結果是True or False來決定保留還是捨棄。

注意到filter()函式返回的是一個Iterator,也就是一個惰性序列,所以要強迫filter()完成計算結果,需要用list()函式獲得所有結果並返回list。

Python實現素數的篩選:

"""定義一個生成器,生成一個3開始的無限奇數序列  
def _odd_iter(): n = 1 while True: n = n + 2 yield n

定義一個篩選函式
def _not_divisible(n):
    return lambda x: x % n > 0
定義一個生成器,不斷返回下一個元素
def primes():
    yield 2 it = _odd_iter() # 初始序列 while True: n = next(it) # 返回序列的第一個數 yield n it = filter(_not_divisible(n), it) # 構造新序列

列印輸出前1000的素數
for n in primes():
    if n < 1000:
        print(n)
    else: break
 

排序函式可以使用sorted()這個函式

>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]

可以自定義一個函式進行排序,傳入函式到key
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]

對字串進行排序,預設是按照ASCII進行排序
>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']

忽略大小寫:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']

預設是從小到大,如果是從大到小
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']

3.2 函式作為返回值

Python中不僅函式可以作為引數傳入,同時,函式也可以作為結果輸出

def lazy_sum(*args):
    def sum():
        ax = 0
        for n in args:
            ax = ax + n
        return ax
    return sum

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x101c6ed90>


>>> f()
25

注意到返回的函式在其定義內部引用了局部變數args,所以,當一個函式返回了一個函式後,其內部的區域性變數還被新函式引用,所以,閉包用起來簡單,實現起來可不容易。

另一個需要注意的問題是,返回的函式並沒有立刻執行,而是直到呼叫了f()才執行。我們來看一個例子

def count():
    fs = []
    for i in range(1, 4):
        def f():
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()

f1(),f2(),f3()的結果全為9,原因是f1(),f2(),f3()是一起執行的。

返回閉包時牢記一點:返回函式不要引用任何迴圈變數,或者後續會發生變化的變數。

 匿名函式lambda是對函式的一種簡化

def f(x):
    return x * x
可以寫成
>>> f = lambda x: x * x

函式模組中的作用域問題:

在一個模組中,我們可能會定義很多函式和變數,但有的函式和變數我們希望給別人使用,有的函式和變數我們希望僅僅在模組內部使用。在Python中,是通過_字首來實現的。

正常的函式和變數名是公開的(public),可以被直接引用,比如:abcx123PI等;

類似__xxx__這樣的變數是特殊變數,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊變數,hello模組定義的文件註釋也可以用特殊變數__doc__訪問,我們自己的變數一般不要用這種變數名;

類似_xxx__xxx這樣的函式或變數就是非公開的(private),不應該被直接引用,比如_abc__abc等;

之所以我們說,private函式和變數“不應該”被直接引用,而不是“不能”被直接引用,是因為Python並沒有一種方法可以完全限制訪問private函式或變數,但是,從程式設計習慣上不應該引用private函式或變數。