Python函數屬性和PyCodeObject

来源:https://www.cnblogs.com/f-ck-need-u/archive/2018/11/09/9937018.html
-Advertisement-
Play Games

函數屬性 python中的函數是一種對象,它有屬於對象的屬性。除此之外,函數還可以自定義自己的屬性。註意,屬性是和對象相關的,和作用域無關。 自定義屬性 自定義函數自己的屬性方式很簡單。假設函數名稱為myfunc,那麼為這個函數添加一個屬性var1: 那麼這個屬性var1就像是全局變數一樣被訪問、修 ...


函數屬性

python中的函數是一種對象,它有屬於對象的屬性。除此之外,函數還可以自定義自己的屬性。註意,屬性是和對象相關的,和作用域無關。

自定義屬性

自定義函數自己的屬性方式很簡單。假設函數名稱為myfunc,那麼為這個函數添加一個屬性var1:

myfunc.var1="abc"

那麼這個屬性var1就像是全局變數一樣被訪問、修改。但它並不是全局變數。

可以跨模塊自定義函數的屬性。例如,在b.py中有一個函數b_func(),然後在a.py中導入這個b.py模塊,可以直接在a.py中設置並訪問來自b.py中的b_func()的屬性。

import b
b.b_func.var1="hello"
print(b.b_func.var1)  # 輸出hello

查看函數對象屬性

python函數是一種對象,是對象就會有對象的屬性。可以通過如下方式查看函數對象的屬性:

dir(func_name)

例如,有一個屬性__name__,它表示函數的名稱:

def f(x):
    y=10
    def g(z):
        return x+y+z
    return g

print(f.__name__)   # 輸出f

還有一個屬性__code__,這個屬性是本文的重點,它表示函數代碼對象:

print(f.__code__)

輸出:

<code object f at 0x0335B180, file "a.py", line 2>

上面的輸出結果已經指明瞭__code__也是對象,既然是對象,它就有自己的屬性:

print( dir(f.__code__) )

現在,就可以看到函數代碼對象相關的屬性,其中有一類屬性都以co_開頭,表示位元組碼的意思,後文會詳細解釋這些屬性的意義。實際上,並非只有函數具有這些屬性,所有的代碼塊(code block)都有這些屬性。

[...省略其它非co_屬性...
'co_argcount', 'co_cellvars',
'co_code', 'co_consts',
'co_filename', 'co_firstlineno',
'co_flags', 'co_freevars',
'co_kwonlyargcount', 'co_lnotab',
'co_name', 'co_names', 'co_nlocals',
'co_stacksize', 'co_varnames']

如何查看這些__code__的屬性?使用f.__code__.co_XXX即可。由於dir()返回的是屬性列表,所以下麵使用迴圈將co_開頭的屬性都輸出出來:

for i in dir(f.__code__):
    if i.startswith("co"):
        print(i+":",eval("f.__code__."+i))

輸出結果:

co_argcount: 1co_cellvars: ('x', 'y')co_code: b'd\x01\x89\x01\x87\x00\x87\x01f\x02d\x02d\x03\x84\x08}\x01|\x01S\x00'co_consts: (None, 10, <code object g at 0x02FB7338, file "g:/pycode/b.py", line 3>, 'f.<locals>.g')
co_filename: g:/pycode/b.py
co_firstlineno: 1
co_flags: 3
co_freevars: ()
co_kwonlyargcount: 0
co_lnotab: b'\x00\x01\x04\x01\x0e\x02'
co_name: f
co_names: ()
co_nlocals: 2
co_stacksize: 3
co_varnames: ('x', 'g')

此外,還可以使用dis模塊的show_code()函數來輸出這些信息的整理:

import dis
def f(x):
    y=10
    def g(z):
        return x+y+z
    return g

print(dis.show_code(f))

輸出結果:

Name:              f
Filename:          g:/pycode/b.py
Argument count:    1
Kw-only arguments: 0
Number of locals:  2
Stack size:        3
Flags:             OPTIMIZED, NEWLOCALS
Constants:
   0: None
   1: 10
   2: <code object g at 0x00A89338, file "g:/pycode/b.py", line 4>
   3: 'f.<locals>.g'
Variable names:
   0: x
   1: g
Cell variables:
   0: x
   1: y
None

__code__屬性的解釋

這些屬性定義在python源碼包的Include/code.h文件中,如有需要,可自行去查看。

另外,這些屬性是代碼塊(code block)的,不限於函數。但此處以函數為例進行說明。

由於這些屬性中涉及到了閉包屬性(或者嵌套函數的屬性),所以以下麵這個a.py文件中的嵌套函數為例:

import dis
x=3
def f(a,b,*args,c):
    a=3
    y=10
    print(a,b,c,x,y)
    def g(z):
        return a+b+c+x+z
    return g

以下是查看函數f()和閉包函數g()的方式:

# f()的show_code結果
dis.show_code(f)

# f()的co_XXX屬性
for i in dir(f.__code__):
    if i.startswith("co"):
        print(i+":",eval("f.__code__."+i))

# 閉包函數,註意,傳遞了*args參數
f1=f(3,4,"arg1","arg2",c=5)

# f1()的show_code結果
dis.show_code(f1)

# f1()的co_XXX屬性
for i in dir(f1.__code__):
    if i.startswith("co"):
        print(i+":",eval("f1.__code__."+i))

下麵將根據上面查看的結果解釋各屬性:

co_name
函數的名稱。

上例中該屬性的值為外層函數f和閉包函數g,註意不是f1。

co_filename
函數定義在哪個文件名中。

上例中為a.py

co_firstlineno
函數聲明語句在文件中的第幾行。即def關鍵字所在的行號。

上例中f()的行號為3,g()的行號為7。

co_consts
該函數中使用的常量有哪些。python中並沒有專門的常量概念,所有字面意義的數據都是常量。

以下是show_code()得到的f()中的常量:

Constants:
   0: None
   1: 3
   2: 10
   3: <code object g at 0x0326B7B0, file "a.py", line 7>
   4: 'f.<locals>.g'

而內層函數g()中沒有常量。

co_kwonlyargcount
keyword-only的參數個數。

f()的keyword-only的參數只有c,所以個數為1
g()中沒有keyword-only類的參數,所以為0

co_argcount
除去*args之外的變數總數。實際上是除去***所收集的參數以及keyword-only類的參數之後剩餘的參數個數。換句話說,是***前面的位置參數個數。

f()中屬於此類參數的有a和b,所以co_argcount數值為2
g()中只有一個位置參數,所以co)argcount數值為1

co_nlocals
co_varnames
本地變數個數和它們的名稱,變數名稱收集在元組中。

f()的本地變數個數為6,元組的內容為:('a', 'b', 'c', 'args', 'y', 'g')
g()的本地變數個數為1,元組的內容為:('z',)

co_stacksize
本段函數需要在棧空間評估的記錄個數。換句話說,就是棧空間個數。

這個怎麼計算的,我也不知道。以下是本示例的結果:
f()的棧空間個數為6
g()的棧空間個數為2

co_names
函數中保存的名稱符號,一般除了本地變數外,其它需要查找的變數(如其它文件中的函數名,全局變數等)都需要保存起來。

f()的co_names:

Names:
   0: print
   1: x

g()的co_names:

Names:
   0: x

co_cellvars
co_freevars
這兩個屬性和嵌套函數(或者閉包有關),它們是互相對應的,所以內容完全相同,它們以元組形式存在。

co_cellvars是外層函數的哪些本地變數被內層函數所引用
co_freevars是內層函數引用了哪些外層函數的本地變數

對外層函數來說,co_freevars一定是空元組,對內層函數來說,co_cellvars則一定是空元組。

如果知道自由變數的概念,這個很容易理解。

f()的co_cellvars內容: ('a', 'b', 'c', 'y')f()的co_freevars內容: ('a', 'b', 'c', 'y')

co_code
co_flags
co_lnotab
這3個屬性和python函數的源代碼編譯成位元組碼有關,本文不解釋它們。

屬性和位元組碼對象PyCodeObject

對於python,通常都認為它是一種解釋型語言。但實際上它在進行解釋之前,會先進行編譯,會將python源代碼編譯成python的位元組碼(bytecode),然後在python virtual machine(PVM)中運行這段位元組碼,就像Java一樣。但是PVM相比JVM而言,要更"高級"一些,這個高級的意思和高級語言的意思一樣:離物理機(處理機器碼)的距離更遠,或者說要更加抽象。

源代碼被python編譯器編譯的結果會保存在記憶體中一個名為PyCodeObject的對象中,當需要運行時,python解釋器開始將其放進PVM中解釋執行,執行完畢後解釋器會"根據需要"將這個編譯的結果對象持久化到二進位文件*.pyc中。下次如果再執行,將首先從文件中載入(如果存在的話)。

所謂"根據需要"是指該py文件是否只運行一次,如果不是,則寫入pyc文件。至少,對於那些模塊文件,都會生成pyc二進位文件。

py文件中的每一個代碼塊(code block)都有一個屬於自己的PyCodeObject對象。每個代碼塊除了被編譯得到的位元組碼數據,還包含這個代碼塊中的常量、變數、棧空間等內容,也就是前面解釋的各種co_XXX屬性信息。

pyc文件包含3部分:

  • 4位元組的Magic int,表示pyc的版本信息
  • 4位元組的int,是pyc的產生時間,如果與py文件修改時間不同,則會重新生成
  • PycodeObject對象序列化的內容

參考文章:https://blog.csdn.net/efeics/article/details/9255193


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前面介紹的數組容納的是一串數字,仿佛一根線把這組數字串了起來,故而它只是一維數組。一維數組用來表示簡單的數列尚可,要是表達複雜的平面坐標系,那就力不從心了。由於平面坐標系存在水平和垂直兩個方向,因此可用二維數組來保存平面坐標繫上的一組坐標頂點,其中第一維是頂點隊列,第二維是頂點的橫縱坐標。許多個平面 ...
  • 函數基礎 函數簡介 定義:就是具有特定功能的一段代碼 優點: 解決代碼的重覆書寫 可以將功能的實現著和使用者分開,提高開發效率 分類: 庫函數:print、input、abs等 自定義:用戶自己封裝的函數 定義:就是具有特定功能的一段代碼 優點: 解決代碼的重覆書寫 可以將功能的實現著和使用者分開, ...
  • 在做項目的過程中經常需要跨域訪問。這裡主要介紹一下 PHP 中怎麼解決跨域問題。 1、允許所有功能變數名稱訪問 2、允許單個功能變數名稱訪問 3、允許多個功能變數名稱訪問 在實際項目中最好指定能跨域訪問的功能變數名稱,增加安全性。可以寫在一個公共類裡面,封裝一個方法調用。 ...
  • 原文:https://webfe.kujiale.com/spring-could-heart/ Spring Cloud 是一個基於 Spring Boot 實現的微服務框架,它包含了實現微服務架構所需的各種組件。 註:Spring Boot 簡單理解就是簡化 Spring 項目的搭建、配置、組合 ...
  • 2018年11月9日,蒟蒻第一次參加NOIP考試,這也是我的第一篇博客。 21:48分,臨睡前,我打開筆記本,在NOIP的前一天晚上,註冊了博客園。發表了第一篇博客。 說是“游記”,但其實並非是真正的“游記”,說起來可能更像“隨筆”,亦或者“感言”。 2018年8月,中考剛剛結束,作為高一的我,正式 ...
  • 基礎很重要,基礎很重要,基礎很重要。重要的事情說三遍,。 程式設計中的控制語句主要有三種:順序、分支和迴圈。我們每天寫的代碼,除了業務相關,裡面會包含大量的控制語句。但是控制語句的基本使用,是否有些坑我們需要避免,以及一些常見的規範我們是否遵守了呢,我們來簡單複習一下。 控制語句包含: 1、分支語句 ...
  • 1 class Base: 2 pass 3 4 class Foo(Base): 5 pass 6 7 class Bar(Foo): 8 pass 9 10 print(issubclass(Bar,Foo)) # True 11 print(issubclass(Foo,Bar)) # Fal ...
  • 相信大家在寫項目的時候遇到過,資料庫里的時間戳類型(時間類型),後臺獲取了,返回前臺,但是前臺的格式不是你想要的格式。例如: 這樣是有毫秒,我在網上找了個簡單又方便的方法: 1、導入JSTL的jar包 2、在jsp頁面加入 3、本來我們輸出是 現在我們輸出是 結果: 最後,如果我們要的格式只要年月日 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...