第一類對象 閉包 迭代器

昨日回顧

函數的定義
        對功能或者動做的封裝.
        語法:
            def 函數名(形參):
                函數體(return)

            函數名(實參)

        return:
            返回. 誰調用就返回到哪裏
            1. 函數中不寫return, 返回None.
            2, 只寫了return, 也是返回None. 結束一個函數的運行
            3, return 值 返回一個值
            4, return 值1, 值2, 值3.... 一次返回多個結果. 打包成元組返回


        參數
            1.形參: 在函數聲明的位置寫的變量
                1. 位置參數
                2. 默認值參數
                3. 動態傳參
                    *   聚合. 接收位置參數的動態傳參
                    **  聚合. 接收關鍵字參數的動態傳參
                參數列表順序:
                    位置 > *args > 默認值 > **kwargs

            2.實參: 函數調用的地方寫的具體的值
                1. 位置參數
                2. 關鍵字參數
                3. 混合參數:  位置 > 關鍵字
                    *  把列表, 字符串, 元組打散成位置參數(實參傳入多個列表的話,每一個列表的前面都要加*)
                    ** 把字典打散成關鍵字參數
        名稱空間:
            1. 內置名稱空間
            2. 全局名稱空間
            3. 局部名稱空間

        做用域:
            1. 全局做用域: 全局名稱空間 + 內置名稱空間
            2. 局部做用域: 局部名稱空間

        globals()  查看全局做用域中的名字,返回的類型是字典
        locals()   查看當前做用域中的名字,返回的類型是字典

        global   在局部引入全局做用域中的內容(全局不存在,幫全局建立)(只要引入了,引入那一層以後用的都是引用的,引用那一層修改了,全局的也會變)
        nonlocal 在局部, 在內層函數中引入離他最近的那一層局部做用域中的變量
View Code

 

今日內容

第一類對象-> 函數名 -> 變量名
        函數對象能夠像變量同樣進行賦值
        還能夠做爲列表的元素進行使用
        能夠做爲返回值返回
        能夠做爲參數進行傳遞

    閉包 -> 函數的嵌套
        內層函數對外層函數中的變量的使用

        好處:
            1. 保護變量不被侵害(#函數內部的變量均可以,由於在函數外部是沒法調用函數內部的)
            2. 讓一個變量常駐內存(#變量被使用就不會被垃圾回收機制回收)

        如何經過代碼查看一個閉包
        函數名.__closure__: 有東西就是閉包. 沒東西就不是閉包

    迭代器 -> 固定的思路. for循環
        一個數據類型中包含了__iter__函數表示這個數據是可迭代的
        dir(數據): 返回這個數據能夠執行的全部操做

        判斷迭代器和可迭代對象的方案(野路子)
        __iter__            可迭代的
        __iter__  __next__  迭代器

        判斷迭代器和可迭代對象的方案(官方)
        from collections import Iterable, Iterator
        isinstance(對象, Iterable) 是不是可迭代的
        isinstance(對象, Iterator) 是不是迭代器

        模擬for循環
        lst= ['a','b','c']
        # 拿迭代器
        it = lst.__iter__()
        while 1:
            try:
                it.__next__()
            except StopIteration:
                break

        特徵:
            1. 省內存(生成器)
            2. 惰性機制
            3. 只能向前. 不能後退

        做用:統一了各類數據類型的遍歷

一.函數名的使用以及第-類對象

def fn():
    print("我叫fn")

fn()
print(fn)  # <function fn at 0x0000000001D12E18>
fn()
gn = fn # 函數名能夠進行賦值
print(gn)
gn()

fn = 666
print(fn) # 666





def func1():
    print("朱祁鎮")

def func2():
    print("徐階")

def func3():
    print("王陽明")

def func4():
    print("魏忠賢")

lst = [func1, func2, func3, func4] # 函數+() 就是調用.
print(lst)  # [<function func1 at 0x1031cee18>, <function func2 at 0x103615f28>, <function func3 at 0x1119c6d90>, <function func4 at 0x1119c6e18>]


# lst[0]()
# for el in lst: #  el是列表中的每一項.
#     el() # 拿到函數. 執行函數



lst = [func1(), func2(), func3(), func4()]  # 存儲函數調用以後的結果
print(lst)  # [None, None, None, None]結果都是None,由於沒有return

# 結果:
# [<function func1 at 0x1031cee18>, <function func2 at 0x103615f28>, <function func3 at 0x1119c6d90>, <function func4 at 0x1119c6e18>]
# 朱祁鎮
# 徐階
# 王陽明
# 魏忠賢
# [None, None, None, None]






a = 10
b = 20
c = 30
lst = [a, b, c]
print(lst)


def wrapper():
    def inner():
        print("個人天, 還能夠扎樣寫")
    print(inner) # <function wrapper.<locals>.inner at 0x00000000028989D8>
    inner()
    return inner

ret = wrapper() # <function wrapper.<locals>.inner at 0x00000000028989D8>
print(ret)
ret()

def wrapper():
    def inner():
        print("哈哈哈")
    return inner  # 函數名能夠像返回值同樣返回

ret = wrapper()
ret() # 在函數外面訪問了函數內部的函數
ret()
ret()


def func1():
    a = 10
    return a
print(func1())




# 函數能夠做爲參數進行傳遞

def func1():
    print("謝晉")

def func2():
    print('楊士奇')

def func3():
    print('徐渭')

def func4():
    print("柳如是")

# 代理. 裝飾器的雛形
def proxy(a): # a就是變量. 形參
    print("我是代理")
    a()
    print("代理執行完畢")

proxy(func1)
proxy(func3)
proxy(func4)
View Code

 

函數名是一個變量, 但它是-一個特殊的變量,與括號配合能夠執行函數的變量.html

1.函數名的內存地址

def func():
    print("呵呵")

print(func)

結果:
<function func at 0x1101e4ea0>

2.函數名能夠賦值給其餘變量

def func():
    print("呵呵")

print(func)

a = func  #把函數當成一個變量賦值給另外一個變量
a()  #函數調用func()

3.函數名能夠當作容器類的元素

def func1():
    print("呵呵")

def func2():
    print("呵呵")

def func3():
    print("呵呵")

def func4():
    print("呵呵")

lst = [func1, func2, func3]
for i in lst:
    i()

4.函數名能夠當作函數的參數

def func():
    print("吃了麼")

def func2(fn):
    print("我是func2")
    fn()#執行傳遞過來的fn
    print("我是func2")

func2(func)#把函數func當成參數傳遞給func2的參數fn.

5.函數名能夠做爲函數的返回值

def func_1():
    print("這裏是函數1")
    def func_2(): 
        print("這裏是函數2")
    print("這裏是函數1")
    return func_2

fn = func_1() #執行函數1. 函數1返回的是函數2,這時fn指向的就是上面函數2
fn() # 執行上面返回的函數    

 

二.閉包

# 閉包的優勢:
#   1, 能夠保護變量不被其餘人侵害
#   2, 保持一個變量常駐內存

def wrapper():
    a = "哈哈" # 不安全的一種寫法
    name = "周杰倫"
    def inner():
        print(name) # 在內層函數中使用了外層函數的局部變量
        print(a)
    def ok():
        nonlocal a
        a = 108
        print(a)
    return inner  # 返回函數名

ret = wrapper()
ret()

def ok():
    global a  # 新建a
    a = 20
    print(a )



def wrapper():  # 記憶這個閉包的寫法
    name = "周杰倫" # 局部變量常駐於內存
    def inner():
        print(name) # 在內層函數中使用了外層函數的局部變量
    return inner  # 返回函數名
    # inner()

ret = wrapper() # ret是一個內層函數
ret() # ret是inner, 執行的時機是不肯定的, 必須保證裏面的name必須存在



# 超級簡易版爬蟲
from urllib.request import urlopen # 導入一個模塊
# 幹掉數字簽名
import ssl
ssl._create_default_https_context = ssl._create_unverified_context


def func():
    # 獲取到網頁中的內容, 當網速很慢的時候. 反覆的去打開這個網站. 很慢
    content = urlopen("https://www.dytt8.net/").read()

    def inner():
        return content.decode("gbk") # 網頁內容
    return inner

print("開始網絡請求")
ret = func() # 網絡請求已經完畢
print("網絡請求完畢")
print("第一次", ret()[5])
print("第二次", ret()[5]) #



def wrapper():
    name = "alex"
    def inner():
        print("胡辣湯")
    print(inner.__closure__) # 查看是不是閉包. 有內容就是閉包, 沒有內容就不是閉包
    inner()

wrapper()
View Code

 

什麼是閉包?閉包就是內層函數,對外層函數(非全局)的變量的引用.叫閉包python

def func1():
    name = "alex"
    def func2():
        print(name)#閉包
    func2()

func1()
結果:
alex

咱們可使用_ closure_ 來檢測函數是不是閉包.使用函數名_ closure_ 返回cell就是閉包.返回None就不是閉包git

def func1():
    name = "alex"
    def func2():
        print( name )#閉包
    func2()
    print(func2.__ .closure__ )# (<cell at 0x10c2e20a8: str object at 0x10c3fc650> ,)

func1()

問題,如何在函數外邊調用內部函數呢?api

def outer():
    name = "alex"
    #內部函數
    def inner():
        print(name)
    return inner

fn = outer() #訪問外部函數,獲取到內部函數的函數地址
fn()#訪問內部函數

那若是多層嵌套呢?很簡單,只須要一層一層的往外層返回就好了安全

def func1():
    def func2():
        def func3():
            print("嘿嘿")
        return func3
    return func2

func1()()()

由它咱們能夠引出閉包的好處.因爲咱們在外界能夠訪問內部函數.那這個時候內部函數訪問的時間和時機就不必定了,由於在外部,我能夠選擇在任意的時間去訪問內部函數.這個時候.想-想.咱們以前說過,若是一個函數執行完畢.則這個函數中的變量以及局部命名空間中的內容都將會被銷燬.在閉包中.若是變量被銷燬了.那內部函數將不能正常執行.因此python規定.若是你在內部函數中訪問了外層函數中的變量.那麼這個變量將不會消亡將會常駐在內存中.也就是說使用閉包,能夠保證外層函數中的變量在內存中常駐.這樣作有什麼好處呢?很是大的好處.咱們來看一個關於爬蟲的代碼:網絡

from urllib. request import urlopen

def but():
    content = urlopen("http://www .xiaohua100. cn/ index . html").read()
    def get_ contentO):
        return content
    return get_ content

fn = but() # 這個時候就開始加載校花100的內容
#後面須要用到這裏面的內容就不須要在執行很是耗時的網絡鏈接操做了
content = fn() # 獲取內容
print(content)
content2 = fn() # 從新獲取內容
print(content2)

綜上,閉包的做用就是讓一個變量可以常駐內存.供後面的程序使用.閉包

 

三.迭代器

s = "今天下午考試. 大家還要睡覺嗎?"

for c in s: # 字符串是一個可迭代對象
    print(c)

for c in "哼哼哈哈":
    print(c)

for i in 123: # 'int' object is not iterable
    print(i)


# dir() 能夠幫咱們查看xxx數據可以執行的操做
print(dir(str)) # __iter__
print(dir(int)) # 沒有__iter__
print(dir(list)) # 有__iter__
print(dir(dict)) # 有__iter__
print(dir(bool)) # 沒有__iter__
for i in True: # 'bool' object is not iterable
    print(i)

print(dir(range))
f = open("呵呵", mode="r", encoding="utf-8")
print(dir(f))

# 共性:全部帶有__iter__的東西均可以進行for循環, 帶有__iter__的東西就是可迭代對象

lst = ["賈樟柯", "李安", "楊德昌", "王家衛"]

# 1. 只能向下執行, 不能反覆
# 2. 結束的時候會給咱們扔出來一個錯誤 StopIteration
# 3. 整合全部的數據類型進行遍歷(int,bool除外)

# print("__iter__" in dir(lst))
it = lst.__iter__()  # 拿到的是迭代器 <list_iterator object at 0x0000000001DCC160>

print(it.__next__()) # 下一個
print(it.__next__()) # 下一個
print(it.__next__()) # 下一個
print(it.__next__()) # 下一個
# print(it.__next__()) # 下一個 # StopIteration 中止迭代
# 想回去
it = lst.__iter__() # 只能從新獲取迭代器

s = {"張無忌", "賈樟柯", "寶寶", "風扇哥", "門神"}
it = s.__iter__()
print(it.__next__())
print(it.__next__())
print(it.__next__())
print(it.__next__())
print(it.__next__())


lst = ["海爾兄弟", "阿童木", "葫蘆娃", "舒克貝塔", "大風車"]
# 模擬for循環 for el in lst:
it = lst.__iter__() # 獲取到迭代器0
while 1: # 循環
    try: # 嘗試
        el = it.__next__() # 那數據
        print(el)
    except StopIteration: # 出了錯誤, 意味着數據拿完了
        break # 結束循環


# 官方經過代碼判斷是不是迭代器
# 藉助於兩個模塊 Iterator迭代器, Iterable可迭代的
from collections import Iterable, Iterator

lst = [1,2,3]
# print(lst.__next__())

print(isinstance(lst, Iterable)) # xxx是不是xxx類型的. True
print(isinstance(lst, Iterator)) # False

it = lst.__iter__()  # 迭代器必定可迭代,  可迭代的東西不必定是迭代器

print(isinstance(it, Iterable)) # xxx是不是xxx類型的. True
print(isinstance(it, Iterator)) # True

for el in it:
    print(el)
View Code

 

咱們以前一直在用可迭代對象進行迭代操做.那麼到底什麼是可迭代對象.本小節主要討論可迭代對象.首先咱們先回顧一下目前咱們所熟知的可迭代對象有哪些:
str, list, tuple, dict, set.那爲何咱們能夠稱他們爲可迭代對象呢?由於他們都遵循了可迭代協議.什麼是可迭代協議.首先咱們先看一段錯誤代碼:app

#對的
s="abc"
for c in s:
    print(c)


#錯的
for i in 123:
    print(i)


結果:
Traceback (most recent call last):
File "/Users/sylar/PychamProjects/oldboy/iterator.py", line 8,in
<module>
for i in 123:
TypeError: 'int' object is not iterable

注意看報錯信息中有這樣一句話. 'int' object is not iterable .翻譯過來就是整數類型對象是不可迭代的. iterable表示可迭代的.表示可迭代協議.那麼如何進行驗證你的數據類型是否符合可迭代協議.咱們能夠經過dir函數來查看類中定義好的全部方法.ssh

s =「個人哈哈哈」
print(dir(s))  #能夠打印對象中的方法和函數
print(dir(str))#也能夠打印類中聲明的方法和函數

在打印結果中.尋找__iter__ 若是能找到. 那麼這個類的對象就是一個可迭代對象.ide

['__add__', '__class__', '__contains__', '__delattr__', '__dir__',
'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__',
'__init_subclass__', __iter__ , '__le__', '__len__', '__lt__', '__mod__',
 '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode',
'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index',
'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower',
'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join',
'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind',
'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines',
'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

咱們發如今字符串中能夠找到__iter__. 繼續看一下list, tuple, dict, set

print(dir(tuple))
print(dir(list)) 
print(dir(open("護⼠士少婦嫩模.txt"))) # 文件對象 
print(dir(set))
print(dir(dict))



# 結果
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 
'__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__',
'__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'count', 'index'] ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__',
'__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
'__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index',
'insert', 'pop', 'remove', 'reverse', 'sort'] ['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__',
'__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__',
'__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed',
'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline',
'readlines', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'writelines'] ['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__',
'__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__',
'__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update',
'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference',
'symmetric_difference_update', 'union', 'update'] ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy',
'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']

咱們發現這幾個能夠進行for循環的東西都有_ iter_ 函數, 包括range也有.能夠本身試一下.


這是查看一個對象是不是可迭代對象的第一種辦法咱們還能夠經過isinstence0函數來查看一個對象是什麼類型的

l = [1,2,3]
l_iter = 1.__ iter__()
from collections import Iterable
from collections import Iterator
print(isinstance(l , Iterable))  #True
print(isinstance(l , Iterator))  #False 
print(isinstance(l_ iter , Iterator))#True
print(isinstance(l_ iter , Iterable))#True

綜上.咱們能夠肯定.若是對象中有__iter__函數. 那麼咱們認爲這個對象遵照了可迭代協議.就能夠獲取到相應的選代器.這裏的__iter__是 幫助咱們獲取到對象的迭代器.
咱們使用迭代器中的__next__()來 獲取到一個迭代器中的元素.

那麼咱們以前講的for的工做原理究竟是什麼?繼續看代碼

s ="我愛北京天安門」
c = s.__iter__() #獲取迭代器

print(c.__next__()) #使用迭代器進行迭代。獲取一個元素我
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #
print(c.__next__()) #StopIteration

for循環的機制:

for i in [1,2,3]:
    print(i)

使用while循環+迭代器來模擬for循環(必需要掌握)

lst = [1,2,3]
lst_ iter = lst.__iter__ ()
while True:
    try:
        i = lst. iter.__ .next___ .(O)
        print(i)
    except StopIteration:
        break

總結:
lterable:可迭代對象.內部包含__iter__()函數
Iterator:迭代器.內部包含__iter__() 同時包含__next__().
迭代器的特色:
  1.節省內存.
  2.惰性機制
  3.不能反覆,只能向下執行.
咱們能夠把要迭代的內容當成子彈.而後呢.獲取到迭代器__iter__ (), 就把子彈都裝在彈夾中而後發射就是__ next__ ()把每一 個 子彈(元素)打出來.也就是說, for循環的時候,一開始的時候是__iter__.()來 獲取迭代器.後面每次獲取元素都是經過__ next__ .()來完成的. 當程序遇到Stoplteration將結束循環.

今日做業

做業

相關文章
相關標籤/搜索