Python基础day4 函数对象、生成器 、装饰器、迭代器、闭包函数
一、函數對象
正確理解 Python函數,能夠幫助我們更好地理解 Python 裝飾器、匿名函數(lambda)、函數式編程等高階技術。
函數(Function)作為程序語言中不可或缺的一部分,太稀松平常了。但函數作為第一類對象(First-Class Object)卻是 Python 函數的一大特性。那到底什么是第一類對象(First-Class Object)呢?
在 Python 中萬物皆為對象,函數作為第一類對象有如下特性:
#函數身為一個對象,擁有對象模型的三個通用屬性:id(內存地址)、類型、和值 def foo(text):return len(text)print(type(foo)) #函數類型 print(id(foo))#函數id 內存地址 print(foo)#函數值?
- 可以作為變量賦值
#!/usr/bin/env python # -*- coding:utf-8 -*- def foo():print('from foo')foo() func=foo #引用,賦值 print(foo) print(func) func()
輸出:
from foo
內存地址一樣說明func引用的是foo函數地址,也就是foo將地址賦值給func變量
<function foo at 0x0000000002063E18>
<function foo at 0x0000000002063E18>
- 可以作為容器類型(集合)的元素
- 作為參數傳遞給其他函數
show(foo)
- 可以作函數的返回值
- 函數嵌套
def f1():#第一步進入f1函數
def f2():#第二部f1函數體中有f2函數聲明
print('from f2')
def f3():#第四部f2函數體中有f3函數聲明
print('from f3')
f3()#第五部f2函數體中運行f3函數
f2()#第三部f1函數體重運行f2內容
f1()
?
二、命名空間與作用域
1.命名空間定義
命名空間是名字和對象的映射,就像是字典,key是變量名,value是變量的值
#定義名字的方法 import time name='egon' #定義變量 def func(): #定義函數passclass Foo:#定義類pass2.命名空間的分類
- 內置名稱空間: 隨著python解釋器的啟動而產生,包括異常類型、內建函數和特殊方法,可以代碼中任意地方調用
- 全局名稱空間:文件的執行會產生全局名稱空間,指的是文件級別定義的名字都會放入該空間
- 局部名稱空間:調用函數時會產生局部名稱空間,只在函數調用時臨時綁定,調用結束解綁定
?
x=10000 #全局 def func():x=1 #局部def f1():pass?
3.作用域
?
- 1. 全局作用域:內置名稱空間,全局名層空間
- 2. 局部作用:局部名稱空間
x=1#全局 def func():x=2#x作用域優先從局部中查找print(x)sum=123123print(sum) func() 查看全局作用域內的名字:gloabls()
查看局局作用域內的名字:locals()
全局作用域的名字:
全局作用域:全局有效,在任何位置都能被訪問到,除非del刪掉,否則會一直存活到文件執行完畢
?
局部作用域的名字:局部有效,只能在函數里(局部范圍調用),只在函數調用時才有效,調用結束就失效
? x=1000def func(y):
x=2
print(locals())#查詢func函數使用的局部{'x': 2, 'y': 1}
print(globals())#查看使用的全局
func(1)
三、閉包函數
1.定義在內部函數,包含對外部作用域非全局作用域的引用,該內部函數就成為閉包函數
?
def f1():x = 1#x全局不可見def f2():print(x)#調用外部作用域xreturn f2#將內部函數返回f=f1()#得到f2 f()# f2()?
2.閉包函數應用:惰性計算
from urllib.request import urlopen#爬床老方法 def index(url):def get():return urlopen(url).read()return get #oldboy= get()函數 存放在內存中還未執行 oldboy=index('http://crm.oldboyedu.com') #調用后才執行,什么時候用什么時候執行 print(oldboy().decode('utf-8')) 閉包函數相對與普通函數會多出一個__closure__的屬性,里面定義了一個元組用于存放所有的cell對象,每個cell對象一一保存了這個閉包中所有的外部變量
# print(oldboy.__closure__[0].cell_contents) res=urlopen('http://crm.oldboyedu.com').read()print(res.decode('utf-8'))
四、裝飾器
1.什么是裝飾器
裝飾別人的工具,修飾添加功能,工具指的是函數,裝飾器本身可以使任何可調用對象,被裝飾的對象也可以使任何可調用對象。
2.為什么要用裝飾器
- 開放封閉原則:對修改時封閉的,對擴招是開放的
- 裝飾器就是為了在不修改被裝飾對象的源代碼以及調用方式的前提下,為其添加新功能
?
裝飾器的功能是將被裝飾的函數當作參數傳遞給與裝飾器對應的函數(名稱相同的函數),并返回包裝后的被裝飾的函數”
直接看示意圖,其中 a 為與裝飾器 @a 對應的函數, b 為裝飾器修飾的函數,裝飾器@a的作用是:
簡而言之:@a 就是將 b 傳遞給 a(),并返回新的 b = a(b)
1.無返回值的修飾器
import time def timmer(func):def wrapper(*args,**kwargs):start_time=time.time()#運行函數前紀錄開始時間func(*args,**kwargs)#裝飾器執行函數(*args,**kwargs)可接受任意長度任意類型stop_time=time.time()#裝飾器函體運行完成后紀錄結束時間print('run time is %s' %(stop_time-start_time))#結束時間減去開始時間就是裝飾器修飾的函數運行時間return wrapper#返回裝飾器函數給timmer@timmer# index = timmer(index) def index():#time.sleep(3)print('welcome to index') if __name__ == '__main__':index()#相當于執行 index = timeer(index)f=timmer(index)
print(f)
f() #wrapper()---->index()
index=timmer(index) #index==wrapper
index() #wrapper()----->
2.有返回值的
?
def timmer(func):def wapper(*args,**kwargs):start_time = time.time()sum = func(*args,**kwargs)end_time = time.time()# print("用時%s"%(end_time-start_time))return sum,(end_time-start_time)#將index函數的計算結果sum以及 用時返回return wapper @timmer def index(x,y=4):time.sleep(1)return x+y if __name__ == '__main__':p = index(3)print(p)?
3.模擬用戶登錄驗證是否有session
#全局變量紀錄登陸狀態 login_user ={"user":None,"status":False} def auth(func):def wrapper(*args,**kwargs):if login_user["user"] and login_user["status"]:#判斷用戶狀態user = none是falseres = func(*args,**kwargs)return reselse:#否則輸入用戶名密碼驗證name = input('user:')password = input("password:")if name == "hanjialong" and password == "123456":#如果驗證通過后在執行login_user["user"] = "hanjialong"login_user["status"] = Trueprint("'\033[45mlogin successful\033[0m'")res = func(*args,**kwargs)return resreturn wrapper @auth def index():print("歡迎登陸") @auth def home(name):print("%s welconme to home page"%name)if __name__ == '__main__':index()home("韓佳龍")4.裝飾器疊加執行順序
?
def timmer(func):def wapper(*args,**kwargs):start_time = time.time()res = func(*args,**kwargs)end_time = time.time()print("用時%s"%(end_time-start_time))return resreturn wapper#全局變量紀錄登陸狀態 login_user ={"user":None,"status":False} def auth(func):def wrapper(*args,**kwargs):if login_user["user"] and login_user["status"]:#判斷用戶狀態user = none是falseres = func(*args,**kwargs)return reselse:#否則輸入用戶名密碼驗證name = input('user:')password = input("password:")if name == "hanjialong" and password == "123456":#如果驗證通過后在執行login_user["user"] = "hanjialong"login_user["status"] = Trueprint("'\033[45mlogin successful\033[0m'")res = func(*args,**kwargs)return resreturn wrapper @auth @timmer def index():print("歡迎登陸") @auth @timmer def home(name):print("%s welconme to home page"%name)if __name__ == '__main__':index()home("韓佳龍")?
5.有參裝飾器
?
user_login = {"user":None,"status":False} def aut(dirver="file"):3#帶參數的裝飾器比不帶參數的多一層 裝飾器最多就三層函數def aut1(fuch):def wrapper(*args,**kwargs):if dirver = "file":if user_login["user"] and user_login["status"]:res = fuch(*args,**kwargs)return reselse:name = input("user:")pwd= input("passworld:")if name = "hanjialong" and pwd = "123456":res = fuch(*args,**kwargs)return reselse:print('\033[45mlogin err\033[0m')elif dirver = 'mysql':if user_login["user"] and user_login["status"]:res = fuch(*args,**kwargs)return reselse:name = input("user:")pwd= input("passworld:")if name = "hanjialong" and pwd = "123456":res = fuch(*args,**kwargs)return reselif dirver = 'ldap':if user_login["user"] and user_login["status"]:res = fuch(*args,**kwargs)return reselse:name = input("user:")pwd= input("passworld:")if name = "hanjialong" and pwd = "123456":res = fuch(*args,**kwargs)return reselse:print("未知認證")return wrapper@auth('file') #@auth2====>index=auth2(index)===>index=auth_wrapper @timmer #index=timmer(auth_wrapper) #index=timmer_wrapper def index():time.sleep(3)print('welcome to index page') @auth(driver='mysql') def home(name):print('%s welcome to home page' %name) index() #timmer_wrapper() # home('egon') #wrapper('egon'?五、迭代器
1.什么是迭代器
重復執行并且迭代結果作為下一次迭代的初始值,這個重復的過程稱為迭代每次重復,并且每次迭代的結果是下一次迭代的初始值
?
#案例1 while True: #只滿足重復,因而不是迭代print('====>')#案例2#以下才是迭代 l = [1, 2, 3] count = 0 while count < len(l): # 只滿足重復,因而不是迭代print('====>', l[count])count += 1 l = (1, 2, 3)
count = 0
while count < len(l): # 只滿足重復,因而不是迭代
print('====>', l[count])
count += 1
s='hello'
count = 0
while count < len(s):
print('====>', s[count])
count += 1
?
2.為什么要使用迭代器
對于沒有索引的數據類型,必須提供一種不依賴索引的迭代方式
可迭代的對象:內置__iter__方法的,都是可迭代的對象
迭代器:執行__iter__方法,得到的結果就是迭代器,迭代器對象有__next__方法
?
?
#有_iter_()方法的都是課迭代對象 # [1,2].__iter__() # 'hello'.__iter__() # (1,2).__iter__() # # {'a':1,'b':2}.__iter__() # {1,2,3}.__iter__()# i=[1,2,3].__iter__()
#
# print(i)
#
# print(i.__next__())
# print(i.__next__())
# print(i.__next__())
# print(i.__next__()) #拋出異常:StopIteration
#使用迭代遍歷字典 dic={'a':1,'b':2,'c':3}
i=dic.__iter__()
while True:
try:
key=i.__next__()
print(dic[key])
except StopIteration:
break
?
3.如何判斷一個對象是可迭代的對象,還是迭代器對象
?
#有iter是可迭代對象 # 'abc'.__iter__() 字符串 # ().__iter__() 元祖tuple # [].__iter__() 列表list # {'a':1}.__iter__() 字典dict # {1,2}.__iter__() 集合set # f=open('a.txt','w') 文件file # f.__iter__()?
- 可迭代對象:只有__iter__方法,執行該方法得到的迭代器對象
from collections import Iterable,Iterator print(isinstance('abc',Iterable)) print(isinstance([],Iterable)) print(isinstance((),Iterable)) print(isinstance({'a':1},Iterable)) print(isinstance({1,2},Iterable)) f = open('a.txt','w') print(isinstance(f,Iterable)) 結果: True True True True True True
- 迭代器對象:對象有__next__,對象有__iter__,對于迭代器對象來說,執行__iter__方法,得到的結果仍然是它本身
1.迭代對象
# 只有文件是迭代器對象 from collections import Iterable,Iterator print(isinstance('abc',Iterator)) print(isinstance([],Iterator)) print(isinstance((),Iterator)) print(isinstance({'a':1},Iterator)) print(isinstance({1,2},Iterator)) f = open('a.txt','w') print(isinstance(f,Iterator)) 結果: False False False False False True2.迭代器
?
# 對象有__iter__,對于迭代器對象來說,執行__iter__方法,得到的結果仍然是它本身 f = open("a.txt","r") t=f.__iter__() print(t) print(f) print(f is t)#f 結果和t結果比較是True # 可迭代對象list,可以看出就是一個迭代器 l = [] i = l.__iter__() print(i) print(i.__iter__()) print(l)dic={'name':'egon','age':18,'height':'180'} # print(dic.items())# for k,v in dic.items(): # print(k,v)#通過迭代器方式 dic={'name':'egon','age':18,'height':'180'} i = dic.__iter__() while True:try:c = next(i)#d得到字典的所以key# print(c)print(dic[c])except StopIteration:break #for循環本身就是迭代器的實現 dic={'name':'egon','age':18,'height':'180'} for i in dic:print(dic[i])l=['a','b',3,9,10] for i in l:print(i)with open('a.txt','r',encoding='utf-8') as f:for line in f:print(line)?
3.迭代器的優缺點
- 優點:
l=[10000,2,3,4,5] i=iter(l) print(next(i)) 2.就迭迭代器本身來說,更節省內存
- 缺點:
1. 無法獲取迭代器對象的長度
2. 不如序列類型取值靈活,是一次性的,只能往后取值,不能往前退
六、生產器函數
只要函數體包含yield關鍵字,該函數就是生成器函數,生成器就是迭代器
1.生成器寫法
普通函數結果只能有一個 def foo():return 1return 2return 3return 4res1=foo() print(res1)res2=foo() print(res2) 結果: 1 1#生成器就是迭代器 def foo():print('first')yield 1print('second')yield 2print('third')yield 3print('fourth')yield 4print('fifth') # print(next(g)) #觸發迭代器g的執行,進而觸發函數的執行 g=foo() for i in g: print(i) 結果: first 1 second 2 third 3 fourth 4 fifth2.生成器應用
def counter(n):print('start...')i=0while i < n:yield ii+=1print('end...')counter(5) g=counter(5) # print(next(g)) # print(next(g)) # print(next(g)) # print(next(g)) for i in g:print(i)總結
a. 相當于為函數封裝好__iter__和__next__
b. return只能返回一次值,函數就終止了,而yield能返回多次值,每次返回都會將函數暫停,下一次next會從上一次暫停的位置繼續執行
?
#模擬#tail -f a.txt | grep 'python'import time def tail(filepath):"""tail功能:param filepath: 文件路徑:return: 相當于return文件最后一行,后等待文件輸入"""with open(filepath,encoding='utf-8') as f:"""seek(offset,whence=0)offset:開始的偏移量,也就是代表需要移動偏移的字節數whence:給offset參數一個定義,表示要從哪個位置開始偏移;0代表從文件開頭開始算起,1代表從當前位置開始算起,2代表從文件末尾算起。默認為0"""f.seek(0,2)while True:line=f.readline().strip()if line:yield lineelse:time.sleep(0.2)# t=tail('a.txt') # # for line in t: # print(line)def grep(pattern,lines):for line in lines:if pattern in line:#如果內容中有patern字符yield line#返回line字符并等待下次輸入# for i in g: # print(i) if __name__ == '__main__':t = tail('a.txt')#返回內存地址g = grep('python',t)for i in g:print(i)?
七、內置函數
| abs() | divmod() | input() | open() | staticmethod() |
| all() | enumerate() | int() | ord() | str() |
| any() | eval() | isinstance() | pow() | sum() |
| basestring() | execfile() | issubclass() | print() | super() |
| bin() | file() | iter() | property() | tuple() |
| bool() | filter() | len() | range() | type() |
| bytearray() | float() | list() | raw_input() | unichr() |
| callable() | format() | locals() | reduce() | unicode() |
| chr() | frozenset() | long() | reload() | vars() |
| classmethod() | getattr() | map() | repr() | xrange() |
| cmp() | globals() | max() | reversed() | zip() |
| compile() | hasattr() | memoryview() | round() | __import__() |
| complex() | hash() | min() | set() | apply() |
| delattr() | help() | next() | setattr() | buffer() |
| dict() | hex() | object() | slice() | coerce() |
| dir() | id() | oct() | sorted() | intern() |
- abs 返回一個數字的絕對值
結果: 1000
- all?如果iterable的所有元素不為0、''、False或者iterable為空,all(iterable)返回True,否則返回False
注意:空元組、空列表返回值為True,這里要特別注意
print(all(['a', 'b', 'c', 'd'])) #列表list,元素都不為空或0 print(all(['a', 'b', '', 'd'])) #列表list,存在一個為空的元素 print(all([0,1,2,3])) #列表list,存在一個為0的元素 print(all(('a', 'b', 'c', 'd'))) #元組tuple,元素都不為空或0 print(all(('a', 'b', '', 'd'))) #元組tuple,存在一個為空的元素 print(all((0,1,2,3))) #元組tuple,存在一個為0的元素 print(all([])) # 空列表 print(all(())) # 空元組結果: True False False True False False True True
- any()
如果所有元素中有一個值非0、''或非False,那么結果就為True,當iterable所有的值都是0、''或False時,那么結果為False
print(any(['a', 'b', 'c', 'd'])) #列表list,元素都不為空或0 print(any(['a', 'b', '', 'd'])) #列表list,存在一個為空的元素 print(any([0,1,2,3])) #列表list,存在一個為0的元素 print(any(('a', 'b', 'c', 'd'))) #元組tuple,元素都不為空或0 print(any(('a', 'b', '', 'd'))) #元組tuple,存在一個為空的元素 print(any((0,1,2,3))) #元組tuple,存在一個為0的元素 print(any([])) # 空列表 print(any(())) # 空元組結果 True True True True True True False False
- ?ascii()
調用對象的__repr__()方法,獲得該方法的返回值
print(ascii([1,2,3,1,22,123])) #[1, 2, 3, 1, 22, 123]
- bin() ?
將十進制數分別轉換為2進制
print(bin(10)) #0b1010- bool()
測試一個對象是True還是False
print(bool([])) #False- bytes()
將一個字符串轉換成字節類型
s="apple" v=bytes(s,encoding="utf-8") print(v) #b'apple'- callable(object)
callable()函數用于測試對象是否可調用,如果可以則返回1(真);否則返回0(假)。可調用對象包括函數、方法、代碼對象、類和已經定義了 調用 方法的類實例。
a = '123' print(callable(a)) #False
- chr(i)
chr()函數返回ASCII碼對應的字符串。
print(chr(65)) #A?
- complex(real[,imaginary])
complex()函數可把字符串或數字轉換為復數。
print(complex(2,1)) #(2+1j)?
- delattr()
刪除對象的屬性
?
- dict()
創建數據字典
?
print(dict()) #{}?
- dir()
不帶參數時返回當前范圍內的變量,方法和定義的類型列表,帶參數時返回參數的屬性,方法列表
?
print(dir()) ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'time']?
- divmod(x,y)
divmod(x,y)函數完成除法運算,返回商和余數。
print(divmod(10,3)) #(3, 1)?
- enumerate()
返回一個可以枚舉的對象,該對象的next()方法將返回一個元組
s = ["a","b","c"] for i ,v in enumerate(s,1):print(i,v) 1 a 2 b 3 c??
- eval()
將字符串str當成有效的表達式來求值并返回計算結果
s = "1 + 3 +5" print(eval(s)) #9?
- exec()
執行字符串或complie方法編譯過的字符串,沒有返回值
?
- float(x)
float()函數把一個數字或字符串轉換成浮點數。
?
print(float("12")) #12.0?
- format()
格式化輸出字符串
print("i am {0},age{1}".format("tom",18)) i am tom,age18?
- frozenset()
創建一個不可修改的集合
? ? ? ?set和frozenset最本質的區別是前者是可變的,后者是不可變的。當集合對象會被改變時(例如刪除,添加元素),只能使用set,
? ? ? ?一般來說使用fronzet的地方都可以使用set。
? ? ? ?參數iterable:可迭代對象。
?
?
- globals()
返回一個描述當前全局變量的字典
a = "apple" print(globals()) {'__package__': None, '__file__': '/Users/hexin/PycharmProjects/py3/day4/2.py', '__name__': '__main__', 'a': 'apple', 'time': <module 'time' (built-in)>, '__cached__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10bd73c88>, '__builtins__': <module 'builtins' (built-in)>, '__spec__': None, '__doc__': None}?
- ?hash()
哈希值hash(object)注意:可哈希的即不可變數據類型,不可哈希即可變數據類型
如果對象object為哈希表類型,返回對象object的哈希值。哈希值為整數,在字典查找中,哈希值用于快遞比價字典的鍵。
兩個數值如果相等,則哈希值也相等。
?
- help()
返回對象的幫助文檔
調用內建的幫助系統,如果不包含參數,交互式幫助系統將在控制臺啟動。如果參數為字串,則可以是模塊,類,方法等名稱,并且幫助頁面將會在控制臺打印。參數也可以為任意對象
?
- hex(x)
hex()函數可把整數轉換成十六進制數。
print(hex(12)) #0xc?
- id()
返回對象的內存地址
a = "apple" print(id(a)) #4562197840?
- input()
獲取用戶輸入內容
?
- int(x[,base])
int()函數把數字和字符串轉換成一個整數,base為可選的基數。
?
- iter()
返回一個iterator對象。
?
- len()函數返回字符串和序列的長度。
?
- list(x)
list()函數可將序列對象轉換成列表。
print(list("hello world")) ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']?
- ?locals()
打印當前可用的局部變量的字典
?
- max(x[,y,z...])
max()函數返回給定參數的最大值,參數可以為序列。
print(max(1,2,3,4)) #4?
- min(x[,y,z...])
min()函數返回給定參數的最小值,參數可以為序列。
print(min(1,2,3,4)) #1?
- next()
返回一個可迭代數據結構(如列表)中的下一項
?
- object()
獲取一個新的,無特性(geatureless)對象。Object是所有類的基類。它提供的方法將在所有的類型實例中共享。
?
- oct(x)
oct()函數可把給出的整數轉換成八進制數。
print(oct(12)) #0o14?
- ord(x)
ord()函數返回一個字符串參數的ASCII碼或Unicode值。
print(ord("a")) #97?
- open()
打開文件open(filename [, mode [, bufsize]])
打開一個文件,返回一個file對象。 如果文件無法打開,將處罰IOError異常
?
- pow(x,y[,z])
pow()函數返回以x為底,y為指數的冪。如果給出z值,該函數就計算x的y次冪值被z取模的值。
print(pow(2,5)) #32 print(pow(2,5,3)) #2?
- range([lower,]stop[,step])
range()函數可按參數生成連續的有序整數列表。
print(range(1,10,2)) #range(1, 10, 2)?
- repr()
將任意值轉換為字符串,供計時器讀取的形式
?
- reversed()
反轉,逆序對象
?
- round(x[,n])
round()函數返回浮點數x的四舍五入值,如給出n值,則代表舍入到小數點后的位數。
print(round(5.9)) #6?
- set()
將對象轉換成集合
?
- slice()
切片功能
s = ["a","b""c","d"] print(slice(1,3,s)) slice(1, 3, ['a', 'bc', 'd'])?
- sorted()
排序
列表排序,按數軸方向排,高階函數,以絕對值大小排序,字符串排序,按照ASCII的大小排序,如果需要排序的是一個元組,則需要使用參數key,也就是關鍵字。反向排序,reserve=True
?
- str(obj)
str()函數把對象轉換成可打印字符串。
print(str(4)) #4?
- sum()
求和
?
- tuple(x)
tuple()函數把序列對象轉換成tuple。
print(tuple("hello world")) ('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd')?
?
- type(obj)
type()函數可返回對象的數據類型。
print(type('123')) print(type(1)) <class 'str'> <class 'int'>?
- vars()
本函數是實現返回對象object的屬性和屬性值的字典對象。如果默認不輸入參數,就打印當前調用位置的屬性和屬性值,相當于locals()的功能。如果有參數輸入,就只打印這個參數相應的屬性和屬性值。
print(vars())#{'__name__': '__main__', '__spec__': None, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, 'time': <module 'time' (built-in)>, '__cached__': None, '__doc__': None, '__file__': '/Users/hexin/PycharmProjects/py3/day4/2.py', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10e5f3c88>} print(vars(time))- ?zip()
將對象逐一配對
s='helloo' l=[1,2,3,4,5]z=zip(s,l) print(z) for i in z:print(i)結果 <zip object at 0x1051d1608> ('h', 1) ('e', 2) ('l', 3) ('l', 4) ('o', 5) ?
轉載于:https://www.cnblogs.com/hanjialong/p/6895698.html
總結
以上是生活随笔為你收集整理的Python基础day4 函数对象、生成器 、装饰器、迭代器、闭包函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Robots at Warehouse(
- 下一篇: bootstrap table教程--使