python基礎(13):函數名的使用、第一類對象、閉包、迭代器

1. 函數名的運用

函數名是⼀個變量,但它是⼀個特殊的變量,與括號配合能夠執⾏函數的變量。

1.1 函數名的內存地址

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

結果:
<function func at 0x1101e4ea0>

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

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

a
= func # 把函數當成⼀個變量賦值給另⼀個變量 a() # 函數調⽤ func()

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

def func1():
  print("呵呵")
def func2():
  print("呵呵")
def func3():
  print("呵呵")
def func4():
  print("呵呵")
lst = [func1, func2, func3]
for i in lst:
  i()

1.4 函數名能夠當作函數的參數

def func():
  print("吃了麼")
def func2(fn):
  print("我是func2")
  fn() # 執⾏傳遞過來的fn
  print("我是func2")

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

1.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() # 執⾏上⾯返回的函數

2. 閉包

什麼是閉包? 閉包就是內層函數,對外層函數(非全局)的變量的引⽤,叫閉包。
def func1():
  name = "alex"
  def func2():
    print(name) # 閉包
  func2()
func1()

結果: alex
咱們可使⽤__closure__來檢測函數是不是閉包,使⽤函數名.__closure__返回cell就是閉包,返回None就不是閉包。
def func1():
  name = "alex"
  def func2():
    print(name) # 閉包
  func2()
  print(func2.__closure__) # (<cell at 0x10c2e20a8: str object at0x10c3fc650>,)
func1()
問題,如何在函數外邊調⽤內部函數呢?
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_content():
    return content
  return get_content
fn = but() # 這個時候就開始加載校花100的內容
# 後⾯須要⽤到這⾥⾯的內容就不須要在執⾏⾮常耗時的⽹絡鏈接操做了
content = fn() # 獲取內容
print(content)

content2
= fn() # 從新獲取內容 print(content2)
綜上,閉包的做⽤就是讓⼀個變量可以常駐內存,供後⾯的程序使⽤。

3. 迭代器

咱們以前⼀直在⽤可迭代對象進⾏迭代操做,那麼到底什麼是可迭代對象。本⼩節主要討
論可迭代對象,⾸先咱們先回顧⼀下⽬前咱們所熟知的可迭代對象有哪些:
str, list, tuple, dict, set. 那爲何咱們能夠稱他們爲可迭代對象呢? 由於他們都遵循了可
迭代協議,什麼是可迭代協議,⾸先咱們先看⼀段錯誤代碼:
# 對的
s = "abc"
for c in s:
  print(c)
# 錯的
for i in 123:
  print(i)

結果: Traceback (most recent call last):    File
"/Users/sylar/PycharmProjects/oldboy/iterator.py", line 8, in <module>    for i in 123: TypeError: 'int' object is not iterable
注意看報錯信息中有這樣⼀句話,'int' object is not iterable 。翻譯過來就是整數類型對象是不可迭代的,iterable表⽰可迭代的,表⽰可迭代協議,那麼如何進⾏驗證你的數據類型是否符合可迭代協議,咱們能夠經過dir函數來查看類中定義好的全部⽅法。
s = "個人哈哈哈"
print(dir(s)) # 能夠打印對象中的⽅法和函數
print(dir(str)) # 也能夠打印類中聲明的⽅法和函數
在打印結果中,尋找__iter__ 若是能找到,那麼這個類的對象就是⼀個可迭代對象。
['__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也有,能夠⾃⼰試⼀下,這是查看⼀個對象是不是可迭代對象的第⼀種辦法,咱們還能夠經過isinstence()函數來查看⼀個對象是什麼類型的。
l = [1,2,3]
l_iter = l.__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__()
    print(i)
  except StopIteration:
    break
總結:
Iterable: 可迭代對象,內部包含__iter__()函數。
Iterator: 迭代器,內部包含__iter__() 同時包含__next__()。
迭代器的特色:
1. 節省內存
2. 惰性機制
3. 不能反覆, 只能向下執⾏
咱們能夠把要迭代的內容當成⼦彈,而後呢,獲取到迭代器__iter__(),就把⼦彈都裝在彈夾中,而後發射就是__next__()把每⼀個⼦彈(元素)打出來。也就是說,for循環的時候,⼀開始的
時候是__iter__()來獲取迭代器,後⾯每次獲取元素都是經過__next__()來完成的,當程序遇到StopIteration將結束循環。
相關文章
相關標籤/搜索