Python隨筆—基礎篇

前言

這篇博文是南國關於python的第一篇博客,之前對於python的學習一直斷斷續續。因爲個人傾向於用Java解決問題,但不可否認Python某些方面有他的優勢和特點。這篇博客 主要回顧下Python之於Java C++不同的基礎知識。本篇博客編寫時許多部分參考了菜鳥教程的講解。話不多說,乾貨來了~

基礎語法

標準數據類型

Python3 中有六個標準的數據類型:

  • Number(數字)
  • String(字符串)
  • List(列表)
  • Tuple(元組)
  • Set(集合)
  • Dictionary(字典)

Python3 的六個標準數據類型中:

  • 不可變數據(3 個):Number(數字)、String(字符串)、Tuple(元組);
  • 可變數據(3 個):List(列表)、Dictionary(字典)、Set(集合)。

數字(Number)類型

python中數字有四種類型:整數、布爾型、浮點數和複數

  • int (整數), 如 1, 只有一種整數類型 int,表示爲長整型,沒有 python2 中的 Long。
  • bool (布爾), 如 True。
  • float (浮點數), 如 1.23、3E-2
  • complex (複數), 如 1 + 2j、 1.1 + 2.2j

內置的 type() 函數可以用來查詢變量所指的對象類型。

>>> a, b, c, d = 20, 5.5, True, 4+3j
>>> print(type(a), type(b), type(c), type(d))
<class 'int'> <class 'float'> <class 'bool'> <class 'complex'>

此外還可以用 isinstance 來判斷:

>>>a = 111
>>> isinstance(a, int)
True

isinstance 和 type 的區別在於:

  • type()不會認爲子類是一種父類類型。
  • isinstance()會認爲子類是一種父類類型。
>>> class A:
...     pass
... 
>>> class B(A):
...     pass
... 
>>> isinstance(A(), A)
True
>>> type(A()) == A 
True
>>> isinstance(B(), A)
True
>>> type(B()) == A
False

關於數值計算:

>>>5 + 4  # 加法
9
>>> 4.3 - 2 # 減法
2.3
>>> 3 * 7  # 乘法
21
>>> 2 / 4  # 除法,得到一個浮點數
0.5
>>> 2 // 4 # 除法,得到一個整數
0
>>> 17 % 3 # 取餘 
2
>>> 2 ** 5 # 乘方
32

注意:
1、Python可以同時爲多個變量賦值,如a, b = 1, 2。
2、一個變量可以通過賦值指向不同類型的對象。
3、數值的除法包含兩個運算符:/ 返回一個浮點數,// 返回一個整數。
4、在混合計算時,Python會把整型轉換成爲浮點數。

字符串(String)

  1. python中單引號和雙引號使用完全相同。
  2. 使用三引號(’’'或""")可以指定一個多行字符串。
  3. 轉義符。反斜槓\可以用來轉義,使用r可以讓反斜槓不發生轉義。。 如 r"this is a line with \n" 則\n會顯示,並不是換行。
  4. 按字面意義級聯字符串,如"this " "is " "string"會被自動轉換爲this is string。
  5. 字符串可以用 + 運算符連接在一起,用 * 運算符重複。
  6. Python 中的字符串有兩種索引方式,從左往右以 0 開始,從右往左以 -1 開始。
  7. Python中的字符串不能改變。
  8. Python 沒有單獨的字符類型,一個字符就是長度爲 1 的字符串。
  9. 字符串的截取的語法格式如下:變量[頭下標:尾下標:步長]

索引值以 0 爲開始值,-1 爲從末尾的開始位置。
在這裏插入圖片描述
加號 + 是字符串的連接符, 星號 * 表示複製當前字符串,與之結合的數字爲複製的次數。實例如下:

'''
@author xjh 2020.3.9
'''
str='python'
print (str)          # 輸出字符串
print (str[0:-1])    # 輸出第一個到倒數第二個的所有字符
print (str[0])       # 輸出字符串第一個字符
print (str[2:5])     # 輸出從第三個開始到第五個的字符
print (str[2:])      # 輸出從第三個開始的後的所有字符
print (str * 2)      # 輸出字符串兩次,也可以寫成 print (2 * str)
print (str + " and pycharm") # 連接字符串

輸出結果:

python
pytho
p
tho
thon
pythonpython
python and pycharm

List 列表

List(列表) 是 Python 中使用最頻繁的數據類型。

列表可以完成大多數集合類的數據結構實現。列表中元素的類型可以不相同,它支持數字,字符串甚至可以包含列表(所謂嵌套)。

列表是寫在方括號 [] 之間、用逗號分隔開的元素列表。

和字符串一樣,列表同樣可以被索引和截取,列表被截取後返回一個包含所需元素的新列表。

列表截取的語法格式如下:
變量[頭下標:尾下標]

索引值以 0 爲開始值,-1 爲從末尾的開始位置。

與Python字符串不一樣的是,列表中的元素是可以改變的:

>>>a = [1, 2, 3, 4, 5, 6]
>>> a[0] = 9
>>> a[2:5] = [13, 14, 15]
>>> a
[9, 2, 13, 14, 15, 6]
>>> a[2:5] = []   # 將對應的元素值設置爲 [] 
>>> a
[9, 2, 6]

注意:
1、List寫在方括號之間,元素用逗號隔開。
2、和字符串一樣,list可以被索引和切片。
3、List可以使用+操作符進行拼接。
4、List中的元素是可以改變的。

下面是list的一個實例:

def reverse(input):
    list=input.split(" ")
    # 翻轉字符串
    # 假設列表 list = [1,2,3,4],
    # list[0]=1, list[1]=2 ,而 -1 表示最後一個元素 list[-1]=4 ( 與 list[3]=4 一樣)
    # inputWords[-1::-1] 有三個參數
    # 第一個參數 -1 表示最後一個元素
    # 第二個參數爲空,表示移動到列表末尾
    # 第三個參數爲步長,-1 表示逆向
    list=list[-1::-1]
    output=' '.join(list)
    return output

if __name__=='__main__':
    classmates = ['kobe', 'James', 'KD']  # 定義一個list,元素集合用[]
    print('classmates=', classmates)
    print(len(classmates))  # 獲取list元素的個數

    classmates.append('Harden')
        #append 在末尾添加元素; 刪除末尾元素用pop(),要刪除指定位置的元素,用pop(i)方法,其中i是索引位置
    print(classmates)
    classmates.insert(2, 'Jordan')  # 在list指定下標位置插入特定元素
    print(classmates)

    s = [123, '123', ['apple', 'Huawei'], 'school']
    print('len: %s' % len(s) + '; s[2]= %s' % s[2])

    input="python java jetbrain"
    print(reverse(input))

輸出結果:

classmates= ['kobe', 'James', 'KD']
3
['kobe', 'James', 'KD', 'Harden']
['kobe', 'James', 'Jordan', 'KD', 'Harden']
len: 4; s[2]= ['apple', 'Huawei']
jetbrain java python

列表的應用有以下幾個方面

  • 將列表作爲堆棧使用;
  • 將列表作爲隊列使用
  • 列表生成式

1.列表作爲堆棧(先進後出)。用 append() 方法可以把一個元素添加到堆棧頂。用不指定索引的 pop() 方法可以把一個元素從堆棧頂釋放出來。

stack=[1,2,3,4,5]
print(stack)
stack.pop()
stack.pop()
print('the result after deleting elements: ',stack)
stack.append(6)
stack.append(7)
t=[10,11,12]
stack.extend(t)
print('the result after adding elements: ',stack)

結果:

[1, 2, 3, 4, 5]
the result after deleting elements:  [1, 2, 3]
the result after adding elements:  [1, 2, 3, 6, 7, 10, 11, 12]

2.列表作爲隊列使用,但是這樣的效率不高。在列表的最後添加或者彈出元素速度快,然而在列表裏插入或者從頭部彈出速度卻不快(因爲所有其他的元素都得一個一個地移動)。

# 隊列使用
queue=deque(['Alex','Bob','Cris','David'])
print(queue)
queue.append('Frank')
queue.append('Gianna')
print('the result after adding elements: ',queue)
queue.popleft()
queue.popleft()
print('the result after deleting elements: ',queue)

結果:

deque(['Alex', 'Bob', 'Cris', 'David'])
the result after adding elements:  deque(['Alex', 'Bob', 'Cris', 'David', 'Frank', 'Gianna'])
the result after deleting elements:  deque(['Cris', 'David', 'Frank', 'Gianna'])

3.列表生成式。最大的優勢在於簡化了代碼編寫量。每個列表推導式都在 for 之後跟一個表達式,然後有零到多個 for 或 if 子句。返回結果是一個根據表達從其後的 for 和 if 上下文環境中生成出來的列表。如果希望表達式推導出一個元組,就必須使用括號。

L1 = ['Hello', 'World', 18, 'Apple', None]
L2 = [x.lower() for x in L1 if isinstance(x, str)] # 列表生成式,將list中所有字符串小寫輸出
print('L2= ',L2)

Tuple 元組

元組tuple,和list非常類似,但是tuple一旦初始化就不能修改,它也沒有append(),insert()這樣的方法。可以通過下標來訪問。

 t1=('KB','LBJ','KD')    #定義一個tuple,元素集合用()
    print('t1= ',t1)
    print(t1[2])

    #“可變”的tuple
    t2=(123,'abc',['A','B'])    #tuple中的元素還可以是list
    print(t2[2][0])
    t2[2][0]='XX'   # 改變的是list中的元素,tuple中的每個指向不變
    print('t2= ',t2)

輸出結果:

t1=  ('KB', 'LBJ', 'KD')
KD
A
t2=  (123, 'abc', ['XX', 'B'])

構造包含 0 個或 1 個元素的元組比較特殊,所以有一些額外的語法規則:

  • tup1 = () # 空元組
  • tup2 = (20,) # 一個元素,需要在元素後添加逗號

Set 集合

集合(set)是由一個或數個形態各異的大小整體組成的,構成集合的事物或對象稱作元素或是成員。

基本功能是進行成員關係測試和刪除重複元素。

可以使用大括號 { } 或者 set() 函數創建集合,注意:創建一個空集合必須用 set() 而不是 { },因爲 { } 是用來創建一個空字典。

創建格式:
parame = {value01,value02,…}
或者
set(value)

Dictionary字典

字典(dictionary)是Python中另一個非常有用的內置數據類型。

列表是有序的對象集合,字典是無序的對象集合。兩者之間的區別在於:字典當中的元素是通過鍵來存取的,而不是通過偏移存取。

字典是一種映射類型,字典用 { } 標識,它是一個無序的 鍵(key) : 值(value) 的集合。

鍵(key)必須使用不可變類型。

在同一個字典中,鍵(key)必須是唯一的。

注意:
1、字典是一種映射類型,它的元素是鍵值對。
2、字典的關鍵字必須爲不可變類型,且不能重複。
3、創建空字典使用 { }。

下面是關於dict和set的一個實例:

'''
dict和set的簡單使用
dict 也被稱爲map,存放key/value元素
set 存放沒有重複的key
set和dict唯一的區別是是否有key對應的value值。
但是set和dict一樣無法存放可變的對象,因爲無法判斷兩個可變對象是否相等
@author xjh 2019.12.04
'''
if __name__=='__main__':
    d={'aa':90,'bb':80,'cc':70,'dd':60,'ee':50} # 定義一個dict 元素集合用{}表示
    print(d['aa']) # 輸出特定key對應的value
    print('cc' in d)   # 判斷某個key是否在dict中存在,in輸出的結果是True/False
    d['aa']=100     # 修改某個key對應的value
    print(d['aa'])  # 輸出特定key對應的value
    print(d)
    d.pop('ee') # pop(key)進行元素刪除
    print(d)

    s=set([1,2,3,3,4,5,5,7])    # 定義一個set,python中用set()表示,這裏使用一個list作爲set的參數
    print(s)    # 輸出set,會自動過濾其中重複的元素
    s.add(10)   # 用add()在set中添加元素,remove()刪除元素
    print(s)

輸出結果:

90
True
100
{'cc': 70, 'ee': 50, 'aa': 100, 'bb': 80, 'dd': 60}
{'cc': 70, 'aa': 100, 'bb': 80, 'dd': 60}
{1, 2, 3, 4, 5, 7}
{1, 2, 3, 4, 5, 7, 10}

關於dict中的元素遍歷:

d={1:'a',2:'b',3:'c'}
for key in d: #迭代key
    print(key)

for value in d.values(): #迭代value
    print(value)

for k,v in d.items(): #同時迭代key value   相當於Java中map的Entry
    print(k,v)

注意:
s.update( “字符串” ) 與 s.update( {“字符串”} ) 含義不同:

  • s.update( {“字符串”} ) 將字符串添加到集合中,有重複的會忽略。
  • s.update( “字符串” ) 將字符串拆分單個字符後,然後再一個個添加到集合中,有重複的會忽略。
>>> thisset = set(("Google", "Runoob", "Taobao"))
>>> print(thisset)
{'Google', 'Runoob', 'Taobao'}
>>> thisset.update({"Facebook"})
>>> print(thisset) 
{'Google', 'Runoob', 'Taobao', 'Facebook'}
>>> thisset.update("Yahoo")
>>> print(thisset)
{'h', 'o', 'Facebook', 'Google', 'Y', 'Runoob', 'Taobao', 'a'}

迭代器和生成器

迭代器 想必大家都很瞭解了,這裏做個簡單回顧:

  • 迭代器是一個可以記住遍歷的位置的對象。
  • 迭代器對象從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。
  • 迭代器有兩個基本的方法:iter() 和 next()。

這裏需要指出來的時把一個類作爲一個迭代器使用需要在類中實現兩個方法 _iter_() 與 next()
_iter_() 方法返回一個特殊的迭代器對象, 這個迭代器對象實現了 _next_() 方法並通過 StopIteration 異常標識迭代的完成。
_next_() 方法(Python 2 裏是 next())會返回下一個迭代器對象。

生成器,Python中 使用了yield的函數稱爲生成器(generator)
跟普通函數不同的是,生成器是一個返回迭代器的函數,只能用於迭代操作,更簡單點理解生成器就是一個迭代器。

在調用生成器運行的過程中,每次遇到 yield 時函數會暫停並保存當前所有的運行信息,返回 yield 的值, 並在下一次執行 next() 方法時從當前位置繼續運行。

調用一個生成器函數,返回的是一個迭代器對象。

輸入輸出

輸出 print

'''
@author xjh 2020.3.9
print 默認輸出是換行的,如果要實現不換行需要在變量末尾加上 end=""
'''

x='a'; y='b'    #Python可以在同一行中使用多條語句,語句之間使用分號(;)分割
print(x); print(y)  #換行輸出
print(x,end=" ");print(y,end="")    #不換行輸出

結果:

a
b
a b

關鍵字end可以用於將結果輸出到同一行,或者在輸出的末尾添加不同的字符。

輸入 input

Python提供了 input() 內置函數從標準輸入讀入一行文本,默認的標準輸入是鍵盤。

input 可以接收一個Python表達式作爲輸入,並將運算結果返回。

#coding=utf-8
def hello():
    return "hello "

name = input("please input your name: "); # 使用input()輸入
print(name)
print(hello()+ "changsha")
print(hello(),"China")

結果:

please input your name: xjh
xjh
hello changsha
hello  China

讀寫文件 open read write

1.打開文件。open() 將會返回一個 file 對象,基本語法格式如下:

  • open(filename, mode)

filename:包含了你要訪問的文件名稱的字符串值。
mode:決定了打開文件的模式:只讀,寫入,追加等。所有可取值見如下的完全列表。這個參數是非強制的,默認文件訪問模式爲只讀®。

2.讀取文件內容。
爲了讀取一個文件的內容,調用 f.read(size), 這將讀取一定數目的數據, 然後作爲字符串或字節對象返回。
size 是一個可選的數字類型的參數。 當 size 被忽略了或者爲負, 那麼該文件的所有內容都將被讀取並且返回。
f.readline() 會從文件中讀取單獨的一行。換行符爲 ‘\n’。f.readline() 如果返回一個空字符串, 說明已經已經讀取到最後一行。

3.寫入文件。f.write(string) 將 string 寫入到文件中, 然後返回寫入的字符數。

# 打開一個文件
f = open("test,txt", "w")

num = f.write( "Python 是一個非常好的語言。\n是的,的確非常好!!\n" )
print(num)
# 關閉打開的文件
f.close()

4.關閉文件。當你處理完一個文件後, 調用 f.close() 來關閉文件並釋放系統的資源,如果嘗試再調用該文件,則會拋出異常。

錯誤和異常

Python中異常處理用的是try…except…finally 乍一看是不是和Java中的try…catch…finally驚人的相似,沒錯。你甚至可以理解爲二者就是講的同一件事。
Python 使用 raise 語句拋出一個指定的異常。你也可以理解爲就是我們平時說的throw。 這裏暫時就不做過多介紹。

面向對象

Python中的類提供了面向對象編程的所有基本功能:類的繼承機制允許多個基類(c++衍生的特點:可多繼承),派生類可以覆蓋基類中的任何方法,方法中可以調用基類中的同名方法
對象可以包含任意數量和類型的數據。

類對象

舉個例子:

class MyClass:
    i=123
    def test(self):
        return 'oop'

x=MyClass()
print(x.i)
print(x.test())

結果:

123
oop

實際上,類有一個名爲 init() 的特殊方法(構造方法),該方法在類實例化時會自動調用。當然,你也可以自己編寫構造方法。
self代表類的實例,而非類。類的方法與普通的函數只有一個特別的區別——它們必須有一個額外的第一個參數名稱, 按照慣例它的名稱是 self。當然,self不是關鍵字,你也可以把它進行替換,但一般很少這樣做。

class Test:
    def prt(self):
        print(self)
        print(self.__class__)
 
t = Test()
t.prt()

結果:

<__main__.Test instance at 0x100771878>
__main__.Test

從執行結果可以很明顯的看出,self 代表的是類的實例,代表當前對象的地址,而 self.class 則指向類。
替換self爲別的,例如temp:

class Test:
    def prt(temp):
        print(temp)
        print(temp.__class__)
 
t = Test()
t.prt()

結果:

<__main__.Test instance at 0x100771878>
__main__.Test

類的方法

如之前的例子所言,使用def來定義一個方法,與一般函數定義不同,類方法必須包含參數 self, 且爲第一個參數,self 代表的是類的實例。

class people:
    #定義基本屬性
    name = ''
    age = 0
    #定義私有屬性,私有屬性在類外部無法直接進行訪問
    __weight = 0
    #定義構造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 說: 我 %d 歲,。" %(self.name,self.age))
 
# 實例化類
p = people('James',35,300)
p.speak()

結果:
James 說: 我 35 歲,200 磅。

繼承

Python支持單繼承 和多繼承:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

需要注意圓括號中父類的順序,若是父類中有相同的方法名,而在子類使用時未指定,python從左至右搜索 即方法在子類中未找到時,從左到右查找父類中是否包含方法。

例如:
1.單繼承demo

class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #調用父類的構造方法
        people.__init__(self,n,a,w)
        self.grade = g
    #覆寫父類的方法
    def speak(self):
        print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade))
s=student('David',10,40,3)
s.speak()

結果:
David 說: 我 10 歲了,我在讀 3 年級

2.多繼承demo

'''
面向對象
@author xjh 2020.3.13
'''
class people:
    #定義基本屬性
    name = ''
    age = 0
    #定義私有屬性,私有屬性在類外部無法直接進行訪問
    __weight = 0
    #定義構造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 說: 我 %d 歲,%d 磅。" %(self.name,self.age,self.__weight))
#單繼承示例
class student(people):
    grade = ''
    def __init__(self,n,a,w,g):
        #調用父類的構造方法
        people.__init__(self,n,a,w)
        self.grade = g
    #覆寫父類的方法
    def speak(self):
        print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade))


# 另一個類,多重繼承之前的準備
class speaker():
    topic = ''
    name = ''
    def __init__(self, n, t):
        self.name = n
        self.topic = t
    def speak(self):
        print("我叫 %s,我是一個演說家,我演講的主題是 %s" % (self.name, self.topic))

# 多重繼承
class sample(speaker, student):
    a = ''
    def __init__(self, n, a, w, g, t):
        student.__init__(self, n, a, w, g)
        speaker.__init__(self, n, t)

p=people('James',35,200)
p.speak()
s=student('David',10,40,3)
s.speak()

test = sample("Tim",25,80,4,"Python")
test.speak()   #方法名同,默認調用的是在括號中排前地父類的方法

結果:

James 說: 我 35 歲,200 磅。
David 說: 我 10 歲了,我在讀 3 年級
我叫 Tim,我是一個演說家,我演講的主題是 Python

類屬性和方法

Python中,如果一個類屬性或者方法以兩個下劃線開頭,則表示私有,不能在類的外部被使用或直接訪問。無,則是公開,可在類的外部直接訪問。
例如:

class JustCounter:
    __secretCount = 0  # 私有變量
    publicCount = 0    # 公開變量
 
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print (self.__secretCount)
 
counter = JustCounter()
counter.count()
counter.count()
print (counter.publicCount)
print (counter.__secretCount)  # 報錯,實例不能訪問私有變量

作用域

Python 中,程序的變量並不是在哪個位置都可以訪問的,訪問權限決定於這個變量是在哪裏賦值的。

變量的作用域決定了在哪一部分程序可以訪問哪個特定的變量名稱。Python的作用域一共有4種,分別是:

有四種作用域:

  • L(Local):最內層,包含局部變量,比如一個函數/方法內部。
  • E(Enclosing):包含了非局部(non-local)也非全局(non-global)的變量。比如兩個嵌套函數,一個函數(或類) A 裏面又包含了一個函數 B ,那麼對於 B 中的名稱來說 A 中的作用域就爲 nonlocal。
  • G(Global):當前腳本的最外層,比如當前模塊的全局變量。
  • B(Built-in): 包含了內建的變量/關鍵字等。,最後被搜索

規則順序: L –> E –> G –> B。

在局部找不到,便會去局部外的局部找(例如閉包),再找不到就會去全局找,再者去內置中找。
舉個例子:Python 的一個內建值 int,我們首先將其賦值爲 0,然後定義一個函數 fun1()。

int = 0
def fun1():
    int = 1
    def fun2():
        int = 2
        print(int)
	fun2()
fun1() #輸出爲2

函數 fun1() 的作用就是調用函數 fun2() 來打印 int 的值。
調用函數fun1() 輸出結果爲2。這是因爲local中的int=2, 函數將其打印出來。

當我們將fun2()中的int=2刪除:

int = 0
def fun1():
    int = 1
    def fun2():
        print(int)
	fun2()
fun1() #輸出爲1

調用函數fun1() 輸出結果爲1。因爲 local 找不到 int 的值,就去上一層 non-local 尋找,發現 int = 1 並打印。

進一步刪除函數 fun1() 中的 int = 1:

int = 0
def fun1():
    def fun2():
        print(int)
	fun2()
fun1() #輸出爲0

因爲 local 和 non-local 都找不到 int 的值,便去 global 中尋找,發現 int = 0 並打印。

若刪除 int = 0這一條件:

def fun1():
    def fun2():
        print(int)
	fun2()
fun1() #輸出爲<class 'int'>

全局變量和局部變量

定義在函數內部的變量擁有一個局部作用域,定義在函數外的擁有全局作用域。
局部變量只能在其被聲明的函數內部訪問,而全局變量可以在整個程序範圍內訪問。調用函數時,所有在函數內聲明的變量名稱都將被加入到作用域中。如下實例:

total = 0 # 這是一個全局變量
# 可寫函數說明
def sum( arg1, arg2 ):
    #返回2個參數的和."
    total = arg1 + arg2 # total在這裏是局部變量.
    print ("函數內是局部變量 : ", total)
    return total
 
#調用sum函數
sum( 10, 20 )
print ("函數外是全局變量 : ", total)

結果:

函數內是局部變量 :  30
函數外是全局變量 :  0

global 和 nonlocal關鍵字

當內部作用域想修改外部作用域的變量時,就要用到global和nonlocal關鍵字了。

num = 1
def fun1():
    global num  # 需要使用 global 關鍵字聲明
    print(num) 
    num = 123
    print(num)
fun1()
print(num)

結果:

1
123
123

如果要修改嵌套作用域(enclosing 作用域,外層非全局作用域)中的變量則需要 nonlocal 關鍵字了,如下實例:

def outer():
    num = 10
    def inner():
        nonlocal num   # nonlocal關鍵字聲明
        num = 100
        print(num)
    inner()
    print(num)
outer()

結果:

100
100

講到nolocal關鍵字, 引申出下一個比較重要的知識點,閉包。

閉包

Python中使用閉包主要是在進行函數式開發使用。他有一個很通俗的理解:如果在一個內部函數裏,對在外部作用域(但不是在全局作用域)的變量進行引用,那麼內部函數就被認爲是閉包(closure).
舉例:

>>>def addx(x):
>>>    def adder(y): return x + y
>>>    return adder
>>> c =  addx(8)
>>> type(c)
<type 'function'>
>>> c.__name__
'adder'
>>> c(10)
18

結合這段簡單的代碼和定義來說明閉包:
如果在一個內部函數裏:adder(y)就是這個內部函數,
對在外部作用域(但不是在全局作用域)的變量進行引用:x就是被引用的變量,x在外部作用域addx裏面,但不在全局作用域裏,
則這個內部函數adder就是一個閉包。

再稍微講究一點的解釋是,閉包=函數塊+定義函數時的環境,adder就是函數塊,x就是環境,當然這個環境可以有很多,不止一個簡單的x。

關於閉包的知識點,在這裏推薦一篇不錯的文章:Python中的閉包

最後寫一個關於閉包的應用demo:

'''
@author xjh 2020.3.7
閉包:如果在一個內部函數裏,對在外部作用域(但不是在全局作用域)的變量進行引用,那麼內部函數就被認爲是閉包(closure)
'''

# 利用閉包返回一個計數器函數,每次調用它返回遞增整數
def createCounter():
    count=0
    def counter():
        nonlocal count  #聲明爲非局部變量
        count+=1
        return count
    return counter

counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA()) # 1 2 3 4 5
counterB = createCounter()
if [counterB(), counterB(), counterB(), counterB()] == [1, 2, 3, 4]:
    print('測試通過!')
else:
    print('測試失敗!')

結果:

1 2 3 4 5
測試通過!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章