為什麼說python內置函數並不是萬能的?

2021-01-10 CDA數據分析師

作者:豌豆花下貓

來源:Python貓

之前我們對比了兩種創建列表的方法,即字面量用法 [] 與內置類型用法 list(),進而分析出它們在運行速度上的差異。

在分析為什麼 list() 會更慢的時候,文中說到它需要經過名稱查找與函數調用兩個步驟,那麼,這就引出了一個新的問題:list() 不是內置類型麼,為什麼它不能直接就調用創建列表的邏輯呢?也就是說,為什麼解釋器必須經過名稱查找,才能「認識」到該做什麼呢?

其實原因很簡單:內置函數/內置類型的名稱並不是關鍵字,它們只是解釋器內置的一種便捷功能,方便開發者開箱即用而已。

PS:內置函數 built-in function 和內置類型 built-in type 很相似,但 list() 實際是一種內置類型而不是內置函數。我曾對這兩種易混淆的概念做過辨析,請查看這篇文章。為了方便理解與表述,以下統稱為內置函數。

1、內置函數的查找優先級最低

內置函數的名稱並不屬於關鍵字,它們是可以被重新賦值的。

比如下面這個例子:

# 正常調用內置函數list(range(3)) # 結果:[0, 1, 2]# 定義任意函數,然後賦值給 listdef test(n): print("Hello World!")list = testlist(range(3)) # 結果:Hello World!

在這個例子中,我們將自定義的 test 賦值給了 list,程序並沒有報錯。這個例子甚至還可以改成直接定義新的同名函數,即"def list(): …"。

這說明了 list 並不是 Python 限定的關鍵字/保留字。

查看官方文檔,可以發現 Python3.9 有35個關鍵字,明細如下:

如果我們將上例的 test 賦值給任意一個關鍵字,例如"pass=test",就會報錯:SyntaxError: invalid syntax。

由此,我們可以從這個角度看出內置函數並不是萬能的:它們的名稱並不像關鍵字那般穩固不變,雖然它們處在系統內置作用域裡,但是卻可以被用戶局部作用域的對象所輕鬆攔截掉!

因為解釋器查找名稱的順序是「局部作用域->全局作用域->內置作用域」,因此內置函數其實是處在最低優先級。

對於新手來說,這有一定的可能會發生意想不到的情況(內置函數有 69 個,要全記住是有難度的)。

那麼,為什麼python 不把所有內置函數的名稱都設為不可複寫的關鍵字呢?

一方面原因是它想控制關鍵字的數量,另一方面可能是想留給用戶更多的自由。內置函數只是解釋器的推薦實現而已,開發者可以根據需要,實現出與內置函數同名的函數。

不過,這樣的場景極少,而且開發者一般會定義成不同名的函數,以 Python 標準庫為例,ast模塊有 literal_eval() 函數(對標 eval() 內置函數)、pprint 模塊有 pprint() 函數(對標 print() 內置函數)、以及itertools模塊有 zip_longest() 函數(對標 zip() 內置函數)……

2、內置函數可能不是最快的

由於內置函數的名稱並非保留的關鍵字,以及它處於名稱查找的末位順序,所以內置函數有可能不是最快的。

上篇文章展示了 [] 比 list() 快 2~3 倍的事實,其實這還可以推廣到 str()、tuple()、set()、dict() 等等內置類型中,都是字面量用法稍稍快於內置類型用法。

對於這些內置類型,當我們調用 xxx() 時,可以簡單理解成正在做類的實例化。在面向對象語言中,類先實例化再使用,這是再正常不過的。

但是,這樣的做法有時也顯得繁瑣。為了方便使用,python 給一些常用的內置類型提供了字面量表示法,也就是""、[]、()、{} 等等,表示字符串、列表、元組和字典等數據類型。

一般而言,所有程式語言都必須有一些字面量表示,但基本都局限在數字類型、字符串、布爾類型以及 null 之類的基礎類型。

Python 中還增加了幾種數據結構類型的字面量,所以是更為方便的,同時這也解釋了為什麼內置函數可能不是最快的。

一般而言,同樣的完備功能,內置函數總是比我們自定義的函數要快,因為解釋器可以做一些底層的優化,例如 len() 內置函數肯定比用戶定義的 x.len() 函數快。

有些人據此形成了「內置函數總是更快」的認識誤區。

解釋器內置函數相對於用戶定義函數,前者接近於走後門;而字面量表示法相對於內置函數,前者是在走更快的後門。

也就是說,在有字面量表示法的情況下,某些內置函數/內置類型並不是最快的!

小結

誠然,python 本身並不是萬能的,那它的任何語法構成部分(內置函數/類型),就更不是萬能的了。但是,一般我們會認為內置函數/類型總歸是「高人一等」的,是受到諸多特殊優待的,顯得像是「萬能的」。

本文從「list() 竟然會敗給 []」破題,從兩個角度揭示了內置函數其實存在著某種不足:內置函數的名稱並不是關鍵字,而內置作用域位於名稱查找的最低優先級,因此在調用時,某些內置函數/類型的執行速度就明顯慢於它們對應的字面量表示法。

相關焦點

  • 為什麼說 Python 內置函數並不是萬能的?
    ) 不是內置類型麼,為什麼它不能直接就調用創建列表的邏輯呢?由此,我們可以從這個角度看出內置函數並不是萬能的:它們的名稱並不像關鍵字那般穩固不變,雖然它們處在系統內置作用域裡,但是卻可以被用戶局部作用域的對象所輕鬆攔截掉
  • 柳小白Python學習筆記 8 函數(function)之內置函數
    函數是組織好的,可重複使用的,用來實現單一或相關聯功能的代碼段。python提供了很多內置函數,當然我們也可以自定義函數。今天主要學習一些內置函數的用法。python裡內置了很多函數,這些函數可以直接調用。
  • Python中有哪些內置函數呢?以及內置函數實例
    >Python中有哪些內置函數呢?常見簡單內置函數:len 求長度min 求最小值max 求最大值sorted 排序reversed 反向sum 求和高級內置函數enumerate 返回一個可以枚舉的對象eval 取出 字符串中的內容,將字符串str當成有效的表達式來求指並返回計算結果exec 執行字符串或complie方法編譯過的字符串
  • python的內置函數:int()轉換成整型
    在python中是利用內置函數int()來將一個對象轉換成整型。python的內置函數int的使用1.內置函數int()語法classint(x,base=10),其中x為一個字符串或數字,base來表示x是以什麼進位的數據來表示的。x數字參數可以是整數、浮點數(小數點表示和指數e表示皆可), 字符串參數僅能包含在指定進位下所涵蓋的字符,該參數可省略。
  • 掌握這兩個方法:助你學會Python 中所有函數(方法)的功能與用法
    python的便利之處不僅僅在於各種多功能強大的封裝庫,更多的是其大量人性化的函數調用,但這一點也是學習python的難點之一。python語言已經接觸過python的讀者應該都對python海量的內置函數有所了解,這些函數把我們原本在C/C++等語言中需要手動實現的功能(如排序/字符串組合/查找)都用最優化的代碼封裝成了內置函數,可供開發者直接調用
  • python內置函數format的使用方法
    Java中的formatdouble totalMoney = 100.23456;String totalM = String.format("%.2f",totalMoney);得到的結果是100.23python內置函數format1.基本語法format 函數可以接受不限個參數,位置可以不按順序
  • python教程:3個非常有用的內置函數
    這三個內置函數還是非常有用的,在工作中用的還不少,順手,下面一一進行介紹 1、filter 語法:filter
  • python的內置函數:sum求和函數
    前言看到sum,我們就知道這是一個求和函數,無論是java、javascript還是mysql中,求和是簡單的,但在python中,可以進行一些複雜的元組求和,具體是怎樣的呢?python中的sum求和函數1.sum的使用語法sum(iterable[, start]) iterable -- 可迭代對象,如:列表、元組、集合。start -- 指定相加的參數,如果沒有設置這個值,默認為0。
  • python的內置函數eval:字符串運算
    前言最先認識eval,是在javascript中,eval() 函數可計算某個字符串,並執行其中的的 JavaScript 代碼。在javascript中,該方法只接受原始字符串作為參數,如果 string 參數不是原始字符串,那麼該方法將不作任何改變地返回。
  • python 列表推導式和生成式,部分內置函數
    >整體說明:凡是用列表推導式構造的列表對象,用其他方式都可構建,但非常複雜的列表,列表推導式是構建不出的,分類:[變量(加工後的變量) for 變量 in iterable] 循環模式[變量(加工後的變量) for 變量 in iterable if 條件] 篩選模式二、生成表達式生成器有兩種方式:1、生成器函數
  • 為什麼 Python 沒有 main 函數?
    其實,可能他們是想模仿真正的main函數,但是許多人都被誤導(或誤解),然後編寫了非常笨拙的代碼。在本文中,我們來討論一下為什麼Python沒有main函數。在開始討論之前,我們先來回答以下兩個問題:所謂的「main函數」究竟是什麼意思?為什麼有些程式語言必須編寫main函數?
  • Python -- 函數對象
    一、函數對象函數是第一類對象:值的內存地址可以像變量一樣去使用def foo():#foo=函數的內存地址,可以當做一種變量值去使用1、函數可以被引用def foo():  print('的內置方法,python啟動的時候,就會定義len()函數,並且把len()丟入內存空間,這個內存空間叫做內置名稱空間生命周期: python啟動的時候就活了,python關閉了就死了2.
  • 清華教授建議吃透python的68個內置函數
    內置函數就是Python給你提供的, 拿來直接用的函數,比如print,input等。截止到python版本3.6.2 ,一共提供了68個內置函數,具體如下 abs() dict() help() min() setattr()all() dir() hex() next() slice() any() divmod() id() object() sorted() ascii() enumerate() input
  • python中函數的入門
    python中函數形式諸如f(x),前面為函數名稱,使用英文的小括號來包裹參數,括號裡面的即為函數的傳入參數。分為內置函數和自定義函數兩類。一、函數分類1.1 內置函數所謂的內置函數,即python語言中常用的、自帶的函數,如print(),len()等。
  • 為什麼繼承 Python 內置類型會出問題?
    在這麼多內容裡,本文只關注那些作為可調用對象(callable)的內置類型,也就是跟內置函數2、內置類型的子類化眾所周知,對於某個普通對象 x,Python 中求其長度需要用到公共的內置函數 len(x),它不像 Java 之類的面向對象語言,後者的對象一般擁有自己的
  • 慢步python,編程中函數的概念,python中函數的聲明和調用
    在編程中,函數和數學中的函數類似,數學中的函數只能是數值的運算轉換,而編程中的函數不僅限於數學運算。在編程中,函數是一段具有特定功能的、可重複使用的代碼組。比如筆者上一篇文章慢步python,說說import,引用功能代碼(功能庫、py文件代碼)中functions.py文件就定義了一個函數,並在另一個程序中調用了。
  • 打基礎一定要吃透這12類 Python 內置函數
    內置函數就是python給你提供的, 拿來直接用的函數,比如print.,input等。截止到python版本3.6.2 python一共提供了68個內置函數,我將它們分成 12 類,方便你學習。1. 和數字相關1.
  • Qt使用C/C++擴展Python內置模塊
    之前和大家介紹過在C/C++中嵌入Python,本次和大家分享下使用C/C++擴展Python內置模塊的方法。被擴展出來的新模塊可以做兩件無法直接在Python中完成的事情:一可以實現新的內置對象類型,二則可以調用C庫函數和一些其他的系統調用。
  • python高階函數:map、filter、reduce的替代品
    什麼是高階函數?高階函數是一種將函數作為參數,或者把函數作為結果返回的函數,map函數、sorted函數就是高階函數的典型例子。map函數在小編以前的文章中做過相應的知識分享。sorted函數是python的內置函數,它的可選參數key用於提供一個函數,它可以將函數應用到各個元素上進行排序。
  • Python中如何創建和調用函數
    第八十五節:創建和調用函數一直以來,數學函數是我輩最大的緊箍咒,現在遇到Python中的函數,就這區區兩個字,竟然一度讓我有了退卻的念頭,鼓起勇氣學了一點點,感覺沒有那麼難,嗯,可以繼續,下面就把今天學習的一點心得分享給大家。不提數學函數了,直接說說Python中的函數的用途。