Python 類元編程(導入時和運行時比較)

導入時和運行時比較

為了正確地做元編程,你必須知道 Python 解釋器什么時候計算各個代碼
塊。Python 程序員會區分“導入時”和“運行時”,不過這兩個術語沒有嚴
格的定義,而且二者之間存在著灰色地帶。在導入時,解釋器會從上到
下一次性解析完 .py 模塊的源碼,然后生成用于執行的字節碼。如果句
法有錯誤,就在此時報告。如果本地的 __pycache__ 文件夾中有最新
的 .pyc 文件,解釋器會跳過上述步驟,因為已經有運行所需的字節碼
了。

編譯肯定是導入時的活動,不過那個時期還會做些其他事,因為 Python
中的語句幾乎都是可執行的,也就是說語句可能會運行用戶代碼,修改
用戶程序的狀態。尤其是 import 語句,它不只是聲明 ,在進程中首
次導入模塊時,還會運行所導入模塊中的全部頂層代碼——以后導入相
同的模塊則使用緩存,只做名稱綁定。那些頂層代碼可以做任何事,包
括通常在“運行時”做的事,例如連接數據庫。 因此,“導入時”與“運行
時”之間的界線是模糊的:import 語句可以觸發任何“運行時”行為。

在前一段中我寫道,導入時會“運行全部頂層代碼”,但是“頂層代碼”會
經過一些加工。導入模塊時,解釋器會執行頂層的 def 語句,可是這么
做有什么作用呢?解釋器會編譯函數的定義體(首次導入模塊時),把
函數對象綁定到對應的全局名稱上,但是顯然解釋器不會執行函數的定
義體。通常這意味著解釋器在導入時定義頂層函數,但是僅當在運行時
調用函數時才會執行函數的定義體。

對類來說,情況就不同了:在導入時,解釋器會執行每個類的定義體,
甚至會執行嵌套類的定義體。執行類定義體的結果是,定義了類的屬性
和方法,并構建了類對象。從這個意義上理解,類的定義體屬于“頂層
代碼”,因為它在導入時運行。

上述說明模糊又抽象,下面通過練習理解各個時期所做的事情。

理解計算時間的練習
假設在 evaltime.py 腳本中導入了 evalsupport.py 模塊。這兩個模塊調用
了幾次 print 函數,打印 <[N]> 格式的標記,其中 N 是數字。下述兩
個練習的目標是,確定各個調用在何時執行。

那兩個模塊的代碼在示例 21-6 和示例 21-7 中。先別運行代碼,拿出紙
和筆,按順序寫出下述兩個場景輸出的標記。
場景 1
在 Python 控制臺中以交互的方式導入 evaltime.py 模塊:

>> import evaltime

場景 2
在命令行中運行 evaltime.py 模塊:

$ python3 evaltime.py

示例 21-6 evaltime.py:按順序寫出輸出的序號標記 <[N]>

from evalsupport import deco_alpha
print('<[1]> evaltime module start')
class ClassOne():print('<[2]> ClassOne body')def __init__(self):print('<[3]> ClassOne.__init__')def __del__(self):print('<[4]> ClassOne.__del__')def method_x(self):print('<[5]> ClassOne.method_x')
class ClassTwo(object):print('<[6]> ClassTwo body')@deco_alpha
class ClassThree():print('<[7]> ClassThree body')def method_y(self):print('<[8]> ClassThree.method_y')
class ClassFour(ClassThree):print('<[9]> ClassFour body')def method_y(self):print('<[10]> ClassFour.method_y')
if __name__ == '__main__':print('<[11]> ClassOne tests', 30 * '.')one = ClassOne()one.method_x()print('<[12]> ClassThree tests', 30 * '.')three = ClassThree()three.method_y()print('<[13]> ClassFour tests', 30 * '.')four = ClassFour()four.method_y()print('<[14]> evaltime module end')

示例 21-7 evalsupport.py:evaltime.py 導入的模塊

print('<[100]> evalsupport module start')
def deco_alpha(cls):print('<[200]> deco_alpha')def inner_1(self):print('<[300]> deco_alpha:inner_1')cls.method_y = inner_1return cls
class MetaAleph(type):print('<[400]> MetaAleph body')def __init__(cls, name, bases, dic):print('<[500]> MetaAleph.__init__')def inner_2(self):print('<[600]> MetaAleph.__init__:inner_2')cls.method_z = inner_2
print('<[700]> evalsupport module end')

場景1的解答
在 Python 控制臺中導入 evaltime.py 模塊后得到的輸出如示例 21-8
所示。
示例 21-8 場景 1:在 Python 控制臺中導入 evaltime 模塊

>>> import evaltime
<[100]> evalsupport module start ?
<[400]> MetaAleph body ?
<[700]> evalsupport module end
<[1]> evaltime module start
<[2]> ClassOne body ?
<[6]> ClassTwo body ?
<[7]> ClassThree body
<[200]> deco_alpha ?
<[9]> ClassFour body
<[14]> evaltime module end ?

? evalsupport 模塊中的所有頂層代碼在導入模塊時運行;解釋
器會編譯 deco_alpha 函數,但是不會執行定義體。
? MetaAleph 類的定義體運行了。
? 每個類的定義體都執行了……
? ……包括嵌套的類。
? 先計算被裝飾的類 ClassThree 的定義體,然后運行裝飾器函
數。
? 在這個場景中,evaltime 模塊是導入的,因此不會運行 if
name == ‘main’: 塊。

對于場景 1,要注意以下幾點。
(1) 這個場景由簡單的 import evaltime 語句觸發。
(2) 解釋器會執行所導入模塊及其依賴(evalsupport)中的每個
類定義體。
(3) 解釋器先計算類的定義體,然后調用依附在類上的裝飾器函
數,這是合理的行為,因為必須先構建類對象,裝飾器才有類對象
可處理。
(4) 在這個場景中,只運行了一個用戶定義的函數或方法
——deco_alpha 裝飾器。

下面來看場景 2。

場景2的解答
運行 python3 evaltime.py 命令后得到的輸出如示例 21-9 所
示。
示例 21-9 場景 2:在 shell 中運行 evaltime.py

$ python3 evaltime.py
<[100]> evalsupport module start
<[400]> MetaAleph body
<[700]> evalsupport module end
<[1]> evaltime module start
<[2]> ClassOne body
<[6]> ClassTwo body
<[7]> ClassThree body
<[200]> deco_alpha
<[9]> ClassFour body ?
<[11]> ClassOne tests ..............................
<[3]> ClassOne.__init__ ?
<[5]> ClassOne.method_x
<[12]> ClassThree tests ..............................
<[300]> deco_alpha:inner_1 ?
<[13]> ClassFour tests ..............................
<[10]> ClassFour.method_y
<[14]> evaltime module end
<[4]> ClassOne.__del__ ?

? 目前為止,輸出與示例 21-8 相同。
? 類的標準行為。
? deco_alpha 裝飾器修改了 ClassThree.method_y 方法,因此
調用 three.method_y() 時會運行 inner_1 函數的定義體。
? 只有程序結束時,綁定在全局變量 one 上的 ClassOne 實例才
會被垃圾回收程序回收。

場景 2 主要想說明的是,類裝飾器可能對子類沒有影響。在示例
21-6 中,我們把 ClassFour 定義為 ClassThree 的子
類。ClassThree 類上依附的 @deco_alpha 裝飾器把 method_y 方
法替換掉了,但是這對 ClassFour 類根本沒有影響。當然,如果
ClassFour.method_y 方法使用 super(…) 調用
ClassThree.method_y 方法,我們便會看到裝飾器起作用,執行
inner_1 函數。

與此不同的是,如果想定制整個類層次結構,而不是一次只定制一
個類,使用下一節介紹的元類更高效。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/92943.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/92943.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/92943.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[git diff] 對比檢查變更 | 提交前復審 | 版本回退

git diff git diff 是 Git 版本控制系統中用于比較文件差異的核心命令&#xff0c;可以顯示工作目錄、暫存區&#xff08;Index&#xff09;和倉庫歷史之間的變化。 通過對比不同版本或狀態的文件內容&#xff0c;幫助開發者理解代碼變更。 比較工作目錄與暫存區 運行以下命令查…

【數據可視化-85】海底撈門店數據分析與可視化:Python + pyecharts打造炫酷暗黑主題大屏

&#x1f9d1; 博主簡介&#xff1a;曾任某智慧城市類企業算法總監&#xff0c;目前在美國市場的物流公司從事高級算法工程師一職&#xff0c;深耕人工智能領域&#xff0c;精通python數據挖掘、可視化、機器學習等&#xff0c;發表過AI相關的專利并多次在AI類比賽中獲獎。CSDN…

物聯網之小白調試網關設備

小伙伴們&#xff0c;你們好呀&#xff01;我是老寇&#xff01;跟我一起學習調試網關設備 相信搞過物聯網的朋友&#xff0c;對網關設備非常熟悉&#xff0c;本人以小白的視角&#xff0c;手把手教你調試網關設備&#xff01; 工作中使用的是Ubuntu操作系統&#xff0c;因此&a…

Node.js特訓專欄-實戰進階:22. Docker容器化部署

?? 歡迎來到 Node.js 實戰專欄!在這里,每一行代碼都是解鎖高性能應用的鑰匙,讓我們一起開啟 Node.js 的奇妙開發之旅! Node.js 特訓專欄主頁 專欄內容規劃詳情 我將從Docker容器化部署的基礎概念入手,介紹Node.js應用容器化的步驟,包括創建Dockerfile、構建鏡像、運行…

eclipse嵌入式編譯速度慢

eclipse 嵌入式 編譯 速度慢 同一個項目&#xff0c;eclipse編譯速度越來越慢&#xff0c;一開始幾秒鐘編譯完&#xff0c;后面要10分鐘。只需要將以下兩個程序卸載重新安裝即可。

編譯Android版本可用的高版本iproute2

背景&#xff1a; Android自帶的iproute2 太老&#xff0c;很多指令格式不支持 直接基于Android源碼&#xff0c;替換源碼下iproute2 代碼編譯新版&#xff0c;報錯太多&#xff0c;于是改用Android NDK工具編譯 環境&#xff1a; android-ndk-r25c-linux.zip 下載鏈接&am…

JavaScript的fetch函數的用法

基本語法fetch函數用于發起網絡請求&#xff0c;返回一個Promise對象。基本語法如下&#xff1a;fetch(url, options).then(response > response.json()).then(data > console.log(data)).catch(error > console.error(Error:, error));GET請求發起一個簡單的GET請求&…

Json和XML文件相互轉化

目錄 一.XML轉Json文件 示例&#xff1a;將XML轉換為JSON 依賴準備 Java代碼示例 代碼詳細講解 二.Json轉XML文件 示例&#xff1a;將JSON轉換為XML 依賴準備 Java代碼示例 代碼詳細講解 關鍵代碼解析 將JSON轉換為XML 寫入文件 示例輸入與輸出 三.具有相同功能的…

Python科學計算與可視化領域工具TVTK、Mayavi、Mlab、Traits(附視頻教程)

概述 TVTK、Mayavi、Mlab 和 Traits 都是 Python 科學計算與可視化領域中緊密相關的工具&#xff0c;它們常被結合使用來處理和展示三維數據。視頻教程&#xff1a;https://pan.quark.cn/s/f73e875225ca 1. TVTK TVTK&#xff08;Traits-based Visualization Toolkit&#xff0…

SQL INSERT INTO SELECT 詳解

SQL INSERT INTO SELECT 詳解 引言 SQL(Structured Query Language)是數據庫操作的基礎語言,廣泛用于各種關系型數據庫管理系統中。在SQL中,INSERT INTO SELECT 是一個強大的功能,它允許用戶從一個表中選取數據,并直接將這些數據插入到另一個表中。本文將詳細講解 SQL …

python速成學習路線

第一部分&#xff1a;核心基礎&#xff08;語法與工具&#xff09; 目標&#xff1a;掌握 Python 的基本語法規則、數據處理方式和開發工具 核心內容&#xff1a; 環境搭建 安裝Python 3.x版本&#xff08;推薦3.10&#xff09;配置開發工具&#xff08;如PyCharm、VS Code或…

自然語言處理的實際應用

在這個信息爆炸的時代&#xff0c;我們每天都在與文字、語音打交道 —— 發送消息、查詢信息、使用智能助手…… 這些看似平常的互動背后&#xff0c;都離不開一項關鍵技術的支撐&#xff1a;自然語言處理&#xff08;NLP&#xff09;。作為人工智能的重要分支&#xff0c;NLP …

Docker實戰:為項目打造即開即用的寶塔LNMP環境

Docker實戰&#xff1a;為項目打造即開即用的寶塔LNMP環境背景一、準備基礎鏡像二、啟動配置容器&#xff08;關鍵步驟&#xff09;三、容器內環境配置&#xff08;逐步執行&#xff09;1. 基礎環境搭建2. 安裝Systemd&#xff08;寶塔依賴&#xff09;3. 安裝寶塔面板&#xf…

.net\c#web、小程序、安卓開發之基于asp.net家用汽車銷售管理系統的設計與實現

.net\c#web、小程序、安卓開發之基于asp.net家用汽車銷售管理系統的設計與實現

藥房智能盤庫系統:基于CV與時間序列預測的庫存革命

> 在醫療資源日益緊張的今天,**全國78%的藥房仍依賴人工盤庫**,平均每100家藥房每年因庫存問題損失超50萬元。當計算機視覺遇見時間序列預測,一場藥房庫存管理的智能化革命正在悄然發生。 --- ### 一、傳統藥房庫存的三大痛點與破局思路 #### 致命痛點分析 1. **人工…

【互動屏幕】解析雙屏聯動在數字展廳中的應用與價值

雙屏聯動 https://www.bmcyzs.com/ 作為現代展廳設計中的重要技術手段&#xff0c;通過兩塊或多塊屏幕的協同工作&#xff0c;實現了信息的動態展示與交互體驗的提升。在展廳環境中&#xff0c;雙屏聯動軟件能夠將觸摸屏與大屏幕無縫連接&#xff0c;使觀眾通過簡單的操作即可控…

clickhouse基礎概念及集群部署

一. 簡述&#xff1a; ClickHouse 是一款高性能列式存儲數據庫&#xff0c;專為海量數據的實時分析場景設計。它以極致的查詢速度、高效的存儲利用率和強大的并行處理能力著稱&#xff0c;廣泛應用于日志分析、用戶行為分析、業務監控等大數據分析領域。1. 核心特性&#xff1a…

低版本 IntelliJ IDEA 使用高版本 JDK 語言特性的問題

現實問題&#xff1a; 目前最新的 IntelliJ IDEA 已經不支持在 Win7 環境上安裝了&#xff0c;如果企業內開發環境仍然是 Win7&#xff0c;就會導致很多問題。 比如當前 IDEA 版本為 2023.1&#xff0c;最大支持 JDK17&#xff0c;如何正常使用 JDK21 的新特性呢&#xff1f;比…

3分鐘 Spring AI 實現對話功能

1.什么是spring AISpring AI 是 Spring 官方推出的一個基于 Spring 生態的 AI 應用開發框架&#xff0c;旨在簡化將人工智能&#xff08;如大語言模型、生成式 AI&#xff09;集成到 Java 應用中的過程。它提供了統一的 API 和工具&#xff0c;讓開發者能更輕松地調用 AI 模型2…

CMake筆記:配置(Configure)、生成(Generate)和構建(Build)

以下為AI生成的內容&#xff1a; 一、配置階段&#xff08;Configure&#xff09; 本質&#xff1a;解析項目邏輯&#xff0c;構建內存模型 觸發命令&#xff1a;cmake -S <源碼路徑> -B <構建路徑> 關鍵操作與輸出&#xff1a;操作類型典型案例輸出產物變量定義se…