py7 函數與變量做用域

目錄

暫缺javascript

函數

定義函數

你能夠定義一個由本身想要功能的函數,如下是簡單的規則:java

  • 函數代碼塊以 def 關鍵詞開頭,後接函數標識符名稱和圓括號 ()
  • 任何傳入參數和自變量必須放在圓括號中間,圓括號之間能夠用於定義參數。
  • 函數的第一行語句能夠選擇性地使用文檔字符串—用於存放函數說明。
  • 函數內容以冒號起始,而且縮進。
  • return [表達式] 結束函數,選擇性地返回一個值給調用方。不帶表達式的return至關於返回 None。

參數傳遞

在 python 中,類型屬於對象,變量是沒有類型的python

a=[1,2,3]c++

a="Runoob"閉包

以上代碼中,[1,2,3] 是 List 類型,"Runoob" 是 String 類型,而變量 a 是沒有類型,她僅僅是一個對象的引用(一個指針),能夠是指向 List 類型對象,也能夠是指向 String 類型對象。app

可更改(mutable)與不可更改(immutable)對象ide

在 python 中,strings, tuples, 和 numbers 是不可更改的對象,而 list,dict 等則是能夠修改的對象。函數

  • 不可變類型:變量賦值 a=5 後再賦值 a=10,這裏實際是新生成一個 int 值對象 10,再讓 a 指向它,而 5 被丟棄,不是改變a的值,至關於新生成了a。優化

  • 可變類型:變量賦值 la=[1,2,3,4] 後再賦值 la[2]=5 則是將 list la 的第三個元素值更改,自己la沒有動,只是其內部的一部分值被修改了。ui

python 函數的參數傳遞:

  • 不可變類型:相似 c++ 的值傳遞,如 整數、字符串、元組。如fun(a),傳遞的只是a的值,沒有影響a對象自己。好比在 fun(a)內部修改 a 的值,只是修改另外一個複製的對象,不會影響 a 自己。

  • 可變類型:相似 c++ 的引用傳遞,如 列表,字典。如 fun(la),則是將 la 真正的傳過去,修改後fun外部的la也會受影響

python 中一切都是對象,嚴格意義咱們不能說值傳遞仍是引用傳遞,咱們應該說傳不可變對象和傳可變對象。

def ChangeInt( a ):
    a = 10
 
b = 2
ChangeInt(b)
print( b ) # 結果是 2
傳不可變對象實例

實例中有 int 對象 2,指向它的變量是 b,在傳遞給 ChangeInt 函數時,按傳值的方式複製了變量 b,a 和 b 都指向了同一個 Int 對象,在 a=10 時,則新生成一個 int 值對象 10,並讓 a 指向它。

# 可寫函數說明
def changeme( mylist ):
   "修改傳入的列表"
   mylist.append([1,2,3,4])
   print ("函數內取值: ", mylist)
   return
 
# 調用changeme函數
mylist = [10,20,30]
changeme( mylist )
print ("函數外取值: ", mylist)
傳可變對象實例

傳入函數的和在末尾添加新內容的對象用的是同一個引用。故輸出結果以下:

函數內取值: [10, 20, 30, [1, 2, 3, 4]]

函數外取值: [10, 20, 30, [1, 2, 3, 4]]

參數

如下是調用函數時可以使用的正式參數類型:

  • 必需參數(位置參數positional參數)
  • 關鍵字參數
  • 默認參數
  • 不定長參數

必需參數(位置參數positional參數)

必需參數須以正確的順序傳入函數。調用時的數量必須和聲明時的同樣

關鍵字參數

關鍵字參數和函數調用關係緊密,函數調用使用關鍵字參數來肯定傳入的參數值。

使用關鍵字參數容許函數調用時參數的順序與聲明時不一致,由於 Python 解釋器可以用參數名匹配參數值。

關鍵字參數必須在位置參數後面,不能出如今它前面,不然報錯

#可寫函數說明
def printme( str ):
   "打印任何傳入的字符串"
   print (str)
   return
 
#調用printme函數
printme( str = "菜鳥教程")

默認參數

調用函數時,若是沒有傳遞參數,則會使用默認參數。如下實例中若是沒有傳入 age 參數,則使用默認值:

def printinfo( name, age = 35 ):
   "打印任何傳入的字符串"
   print ("名字: ", name)
   print ("年齡: ", age)
   return
 
#調用printinfo函數
printinfo( age=50, name="runoob" )
print ("------------------------")
printinfo( name="runoob" )

以上實例輸出結果:

名字: runoob 年齡: 50 ------------------------ 名字: runoob 年齡: 35

不定長參數

可變長指的是實參值的個數不固定
        而實參有按位置和按關鍵字兩種形式定義,針對這兩種形式的可變長,形參對應有兩種解決方案來完整地存放它們,分別是*args,**kwargs

加了星號 * 的參數會以元組(tuple)的形式導入,存放全部未命名的變量參數。

若是在函數調用時沒有指定參數,它就是一個空元組。咱們也能夠不向函數傳遞未命名的變量。

加了兩個星號 ** 的參數會以字典的形式導入。

特殊的:

def func(*args,**kwargs)定義了不定長參數,傳參時,能夠直接傳元組和字典內的格式也可直接傳列表和字典可是傳列表和字典時,前面必定要加*號,以下 li = [11,2,2,3,3,4,54] func(*li) di = {'name':'wupeiqi', age:18, 'gender':'male'} func(*

不定長參數的嵌套和使用問題

def func(*a, **k):

    def fun1(*a, **k):  # 先看fun2
        print(a)
        print(k)
        pass
    print(a)
    fun1(a, k)
    fun1((465,), 789, {})
    fun1(*a, **k)

    def fun2(*a, **k):
        print('fun2 a--', a)  # a表明*a接收的元素的元組形式
        print('fun2 k--', k)  # k表明**k接收的元素的字典形式
        print('fun2 *a--', *a)   # *a表明*a接收的內容(原始內容,不是元組)
        print('fun2 **k--', **k)  # **k表明**k接收的內容(原始內容,不是字典)

        pass
    fun2(*a, **k)    # *a也是傳遞*a包含的原始內容,([1,2,3,4])若如此傳參,*a接收的就是([1,2,3,4]),   # 在列表,元組前面加*至關於取列表或者元組或者字典的內容
    # 能夠(*[1,2,3,4]),則*a接收的就是(1,2,3,4)
   # 在列表,元組前面加*至關於取列表或者元組或者字典的內容
func(123) c = ((123,),) print(c) print(c.__len__() ) 輸出結果爲 (123,) ((123,), {}) {} ((465,), 789, {}) {} (123,) {} fun2 a-- (123,) fun2 k-- {} fun2 *a-- 123 fun2 **k-- ((123,),) 1

 

        ===========*args===========
        def foo(x,y,*args):
            print(x,y)
            print(args)
        foo(1,2,3,4,5)

        def foo(x,y,*args):
            print(x,y)
            print(args)
        foo(1,2,*[3,4,5])


        def foo(x,y,z):
            print(x,y,z)
        foo(*[1,2,3])

        ===========**kwargs===========
        def foo(x,y,**kwargs):
            print(x,y)
            print(kwargs)
        foo(1,y=2,a=1,b=2,c=3)

        def foo(x,y,**kwargs):
            print(x,y)
            print(kwargs)
        foo(1,y=2,**{'a':1,'b':2,'c':3})


        def foo(x,y,z):
            print(x,y,z)
        foo(**{'z':1,'x':2,'y':3})

        ===========*args+**kwargs===========

        def foo(x,y):
            print(x,y)

        def wrapper(*args,**kwargs):
            print('====>')
            foo(*args,**kwargs)

#五、命名關鍵字參數:*後定義的參數,必須被傳值(有默認值的除外),且必須按照關鍵字實參的形式傳遞
能夠保證,傳入的參數中必定包含某些關鍵字
        def foo(x,y,*args,a=1,b,**kwargs):
            print(x,y)
            print(args)
            print(a)
            print(b)
            print(kwargs)

        foo(1,2,3,4,5,b=3,c=4,d=5)
        結果:
            2
            (3, 4, 5)
            3
            {'c': 4, 'd': 5}

此乃重點知識!!!
可變長參數

閉包函數

一 什麼是閉包?

內部函數包含對外部做用域而非全局做用域的引用

提示:以前咱們都是經過參數將外部的值傳給函數,閉包提供了另一種思路,包起來嘍,包起呦,包起來哇

        def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n+=1
                return x
            return incr

        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #查看閉包的元素
閉包函數

二 閉包的意義與應用

閉包的意義:返回的函數對象,不只僅是一個函數對象,在該函數外還包裹了一層做用域,這使得,該函數不管在何處調用,優先使用本身外層包裹的做用域

應用領域:延遲計算(原來咱們是傳參,如今咱們是包起來)

    from urllib.request import urlopen

    def index(url):
        def get():
            return urlopen(url).read()
        return get

    baidu=index('http://www.baidu.com')
    print(baidu().decode('utf-8'))
用法

尾調用函數

普通的函數遞歸或者函數調用時,由於外層函數並未結束,因此有一個」調用棧「的概念去儲存函數的調用狀態,這無形會浪費大量空間時間,無形使代碼效率變低。

可是利用尾調函數進行優化的話,即在函數的最後一步(不必定是最後一行)調用別的函數,不會生成「調用」棧,由於在函數的最後一步調用別的函數的話原函數的狀態不會保留,至關於原函數已經結束。

這裏要注意:return fun(x)是尾調,而 return fun(x)+1不是,由於後者至關於又把調用後的結果賦值給了return,因此調用不是最後一步。不寫return在函數尾部直接調用函數也是尾調。

return語句

return [表達式] 語句用於退出函數,選擇性地向調用方返回一個表達式。不帶參數值的return語句(或不寫return)返回None

def test1():
    print('1')

def test2():
    print('2')
    return 0

def test3():
    print('3')
    return 0, 'qwe',[1,'qwee',[]],{'1':'dsf'}

def test4():
    print('4')
    return test2

def test5():
    print('5')
    return test2()


a = test1()
b = test2()
c = test3()
d = test4()
e = test5()
print(a)
print(b)
print(c)
print(d)
print(e)


1
2
3
None
<function test3 at 0x000001672A31A7B8>
(0, 'qwe', [1, 'qwee', []], {'1': 'dsf'})
return的各類返回值

變量做用域

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

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

  • L (Local) 局部做用域
  • E (Enclosing) 閉包函數外的函數中
  • G (Global) 全局做用域
  • B (Built-in) 內建做用域

以 L –> E –> G –>B 的規則查找,即:在局部找不到,便會去局部外的局部找(例如閉包),再找不到就會去全局找,再者去內建中找。

x = int(2.9)  # 內建做用域
 
g_count = 0  # 全局做用域
def outer():
    o_count = 1  # 閉包函數外的函數中
    def inner():
        i_count = 2  # 局部做用域

Python 中只有模塊(module),類(class)以及函數(def、lambda)纔會引入新的做用域,其它的代碼塊(如 if/elif/else/、try/except、for/while等)是不會引入新的做用域的

也就是說這些語句內定義的變量,外部也能夠訪問,若是將變量定義在函數中,則它就是局部變量,外部不能訪問

全局變量和局部變量

定義在函數內部的變量擁有一個局部做用域,定義在函數外的擁有全局做用域。

global 和 nonlocal關鍵字

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

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

以上實例輸出結果:
1
123
123
global用法

若是要修改嵌套做用域(enclosing 做用域,外層非全局做用域)中的變量則須要 nonlocal 關鍵字了:

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

以上實例輸出結果:
100
100
nonlocal用法

注:另外有一種特殊狀況,假設下面這段代碼被運行:

a = 10
def test():  # 給函數設定一個參數a就行了
    a = a + 1
    print(a)
test()

以上程序執行,報錯信息以下:UnboundLocalError: local variable 'a' referenced before assignment

錯誤信息爲局部做用域引用錯誤,由於 test 函數中的 a 使用的是局部,未定義,沒法修改。

內置函數

函數對象(函數也是對象)

秉承着一切皆對象的理念,咱們再次回頭來看函數(function)。函數也是一個對象,具備屬性(可使用dir()查詢)。

做爲對象,它還能夠賦值給其它對象名,或者做爲參數傳遞。

lambda函數(匿名函數)

關鍵字lambda表示匿名函數,冒號前面的表示函數參數。

匿名函數有個限制,就是隻能有一個表達式,不用寫return,返回值就是該表達式的結果。

用匿名函數有個好處,由於函數沒有名字,沒必要擔憂函數名衝突。此外,匿名函數也是一個函數對象,也能夠把匿名函數賦值給一個變量,再利用變量來調用該函數:

>>> f = lambda x: x * x
>>> f
<function <lambda> at 0x101c6ef28> >>> f(5) 25 

一樣,也能夠把匿名函數做爲返回值返回,好比:

def build(x, y): return lambda: x * x + y * y

兩個參數的匿名函數

func = lambda x,y: x + y
print func(3,4)
lambda生成一個函數對象。該函數參數爲x,y,返回值爲x+y。函數對象賦給func。func的調用與正常函數無異。

以上定義能夠寫成如下形式:

def func(x, y):
    return x + y

 

函數做爲參數傳遞

函數能夠做爲一個對象,進行參數傳遞。函數名(好比func)即該對象。好比說:

def test(f, a, b):
    print 'test'
    print f(a, b)

test(func, 3, 5)

test函數的第一個參數f就是一個函數對象。將func傳遞給f,test中的f()就擁有了func()的功能。

咱們所以能夠提升程序的靈活性。可使用上面的test函數,帶入不一樣的函數參數。好比:

test((lambda x,y: x**2 + y), 6, 9)

 

map()函數

map()是Python的內置函數。它的第一個參數是一個函數對象。

re = map((lambda x: x+3),[1,3,5,6])
這裏,map()有兩個參數,一個是lambda所定義的函數對象,一個是包含有多個元素的表。map()的功能是將函數對象依次做用於表的每個元素,每次做用的結果儲存於返回的表re中。map經過讀入的函數(這裏是lambda函數)來操做數據(這裏「數據」是表中的每個元素,「操做」是對每一個數據加3)。

在Python 3.X中,map()的返回值是一個可迭代對象。能夠利用list()函數,將該循環對象轉換成表。

若是做爲參數的函數對象有多個參數,可以使用下面的方式,向map()傳遞函數參數的多個參數:


re = map((lambda x,y: x+y),[1,2,3],[6,7,9])
map()將每次從兩個表中分別取出一個元素,帶入lambda所定義的函數。

 

filter()函數

filter函數的第一個參數也是一個函數對象。它也是將做爲參數的函數對象做用於多個元素。若是函數對象返回的是True,則該次的元素被儲存於返回的表中。filter經過讀入的函數來篩選數據。一樣,在Python 3.X中,filter返回的不是表,而是循環對象。

filter函數的使用以下例:

def func(a):
    if a > 100:
        return True
    else:
        return False

print filter(func,[10,56,101,500])

 

reduce()函數

reduce函數的第一個參數也是函數,但有一個要求,就是這個函數自身能接收兩個參數。reduce能夠累進地將函數做用於各個參數。以下例:

print reduce((lambda x,y: x+y),[1,2,5,7,9])
reduce的第一個參數是lambda函數,它接收兩個參數x,y, 返回x+y。

reduce將表中的前兩個元素(1和2)傳遞給lambda函數,獲得3。該返回值(3)將做爲lambda函數的第一個參數,而表中的下一個元素(5)做爲lambda函數的第二個參數,進行下一次的對lambda函數的調用,獲得8。依次調用lambda函數,每次lambda函數的第一個參數是上一次運算結果,而第二個參數爲表中的下一個元素,直到表中沒有剩餘元素。

上面例子,至關於(((1+2)+5)+7)+9

根據mmufhy的提醒: reduce()函數在3.0裏面不能直接用的,它被定義在了functools包裏面,須要引入包,見評論區。

實現了 __call__ 的類也能夠做爲函數

對於一個自定義的類,若是實現了 __call__ 方法,那麼該類的實例對象的行爲就是一個函數,是一個能夠被調用(callable)的對象。例如:

class Add:
   def __init__(self, n):
        self.n = n
   def __call__(self, x):
       return self.n + x

>>> add = Add(1)
>>> add(4)
>>> 5

執行 add(4) 至關於調用 Add.__call__(add, 4),self 就是實例對象 add,self.n 等於 1,因此返回值爲 1+4

add(4)
  ||
Add(1)(4)
  ||
Add.__call__(add, 4)
相關文章
相關標籤/搜索