目錄
" >##迭代器的大小
自省(introspection)" >自省(introspection)
魔法属性" >魔法属性
创建自己魔法方法?" >创建自己魔法方法?
首頁 後端開發 Python教學 Python中的魔法方法

Python中的魔法方法

Apr 13, 2023 am 10:25 AM
python 運算符

python中的魔法方法是一些可以讓你對類別添加「魔法」的特殊方法,它們經常是兩個下劃線包圍來命名的。

Python中的魔法方法

Python的魔法方法,也稱為dunder(雙底線)方法。大多數的時候,我們將它們用於簡單的事情,例如建構函數(init)、字串表示(str, repr)或算術運算子(add/mul)。其實還有許多你可能沒聽過的但是卻很好用的方法,在這篇文章中,我們將整理這些魔法方法!

##迭代器的大小

#我們都知道__len__方法,可以用它在容器類別上實作len()函數。但是,如果您想要取得實作迭代器的類別物件的長度怎麼辦?

it = iter(range(100))
 print(it.__length_hint__())
 # 100
 next(it)
 print(it.__length_hint__())
 # 99
 
 a = [1, 2, 3, 4, 5]
 it = iter(a)
 print(it.__length_hint__())
 # 5
 next(it)
 print(it.__length_hint__())
 # 4
 a.append(6)
 print(it.__length_hint__())
 # 5
登入後複製

你所需要做的就是實作__length_hint__方法,這個方法是迭代器上的內建方法(不是產生器) ,正如你上面看到的那樣,並且還支援動態長度變更。但是,正如他的名字那樣,這只是一個提示(hint),並不能保證完全準確:對於列表迭代器,可以得到準確的結果,但是對於其他迭代器則不確定。但是即使它不準確,它也可以幫助我們獲得需要的信息,正如PEP 424中所解釋的那樣。

length_hint must return an integer (else a TypeError is raised) or NotImplemented, and is not required to be accurate.  It may return a value that is either be accurate. larIt may return a value that is either be accurate. larIt may return a value that is either be accurate. the actual size of the container.  A return value of NotImplemented indicates that there is no finite length estimate.  It may not return a negative value (else# a ValueError is#ised).#". #元程式設計

大部分很少看到的神奇方法都與元程式設計有關,雖然元程式設計可能不是我們每天都需要使用的東西,但有一些方便的技巧可以使用它。

一個這樣的技巧是使用__init_subclass__作為擴展基類功能的快捷方式,而不必處理元類:
 class Pet:
def __init_subclass__(cls, /, default_breed, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_breed = default_breed
 
 class Dog(Pet, default_name="German Shepherd"):
pass
登入後複製

上面的代碼我們向基類添加關鍵字參數,該參數可以在定義子類別時設定。在實際用例中可能會在想要處理提供的參數而不僅僅是賦值給屬性的情況下使用此方法。

看起來非常晦澀並且很少會用到,但其實你可能已經遇到過很多次了,因為它一般都是在構建API時使用的,例如在SQLAlchemy或Flask Views中都使用到了。

另一個元類別的神奇方法是__call__。這個方法允許自訂呼叫類別實例時發生的事情:

 class CallableClass:
def __call__(self, *args, **kwargs):
print("I was called!")
 
 instance = CallableClass()
 
 instance()
 # I was called!
登入後複製

可以用它來建立一個不能被呼叫的類別:

 class NoInstances(type):
def __call__(cls, *args, **kwargs):
raise TypeError("Can't create instance of this class")
 
 class SomeClass(metaclass=NoInstances):
@staticmethod
def func(x):
print('A static method')
 
 instance = SomeClass()
 # TypeError: Can't create instance of this class
登入後複製

對於只有靜態方法的類,不需要建立類別的實例就用到了這個方法。

另一個類似的場景是單例模式-一個類別最多只能有一個實例:

 class Singleton(type):
def __init__(cls, *args, **kwargs):
cls.__instance = None
super().__init__(*args, **kwargs)
 
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
else:
return cls.__instance
 
 class Logger(metaclass=Singleton):
def __init__(self):
print("Creating global Logger instance")
登入後複製

Singleton類別擁有一個私有__instance-如果沒有,它會被建立並賦值,如果它已經存在,它只會被回傳。

假設有一個類,你想建立它的一個實例而不呼叫__init__。 __new__ 方法可以幫助解決這個問題:

 class Document:
def __init__(self, text):
self.text = text
 
 bare_document = Document.__new__(Document)
 print(bare_document.text)
 # AttributeError: 'Document' object has no attribute 'text'
 
 setattr(bare_document, "text", "Text of the document")
登入後複製

在某些情況下,我們可能需要繞過建立實例的通常過程,上面的程式碼示範如何做到這一點。我們不呼叫Document(…),而是呼叫Document.__new__(Document),它建立一個裸實例,而不呼叫__init__。因此,實例的屬性(在本例中為text)沒有初始化,所欲我們需要額外使用setattr函數賦值(它也是一個魔法的方法__setattr__)。

為什麼要這麼做呢。因為我們可能會想要替代建構函數,例如:

 class Document:
def __init__(self, text):
self.text = text
 
@classmethod
def from_file(cls, file): # Alternative constructor
d = cls.__new__(cls)
# Do stuff...
return d
登入後複製

這裡定義from_file方法,它作為建構函數,首先使用__new__建立實例,然後在不呼叫__init__的情況下配置它。

下一個與元程式設計相關的神奇方法是__getattr__。當普通屬性存取失敗時呼叫此方法。這可以用來將對缺失方法的存取/呼叫委託給另一個類別:

 class String:
def __init__(self, value):
self._value = str(value)
 
def custom_operation(self):
pass
 
def __getattr__(self, name):
return getattr(self._value, name)
 
 s = String("some text")
 s.custom_operation() # Calls String.custom_operation()
 print(s.split()) # Calls String.__getattr__("split") and delegates to str.split
 # ['some', 'text']
 
 print("some text" + "more text")
 # ... works
 print(s + "more text")
 # TypeError: unsupported operand type(s) for +: 'String' and 'str'
登入後複製

我們想為類別添加一些額外的函數(如上面的custom_operation)定義string的自訂實作。但我們並不想重新實作每一個字串方法,像是split、join、capitalize等等。這裡我們就可以使用__getattr__來呼叫這些現有的字串方法。

雖然這適用於普通方法,但請注意,在上面的範例中,魔法方法__add__(提供的連接等操作)沒有委託。所以,如果我們想讓它們也能正常運作,就必須重新實現它們。

自省(introspection)

最后一个与元编程相关的方法是__getattribute__。它一个看起来非常类似于前面的__getattr__,但是他们有一个细微的区别,__getattr__只在属性查找失败时被调用,而__getattribute__是在尝试属性查找之前被调用。

所以可以使用__getattribute__来控制对属性的访问,或者你可以创建一个装饰器来记录每次访问实例属性的尝试:

 def logger(cls):
original_getattribute = cls.__getattribute__
 
def getattribute(self, name):
print(f"Getting: '{name}'")
return original_getattribute(self, name)
 
cls.__getattribute__ = getattribute
return cls
 
 @logger
 class SomeClass:
def __init__(self, attr):
self.attr = attr
 
def func(self):
...
 
 instance = SomeClass("value")
 instance.attr
 # Getting: 'attr'
 instance.func()
 # Getting: 'func'
登入後複製

装饰器函数logger 首先记录它所装饰的类的原始__getattribute__方法。然后将其替换为自定义方法,该方法在调用原始的__getattribute__方法之前记录了被访问属性的名称。

魔法属性

到目前为止,我们只讨论了魔法方法,但在Python中也有相当多的魔法变量/属性。其中一个是__all__:

 # some_module/__init__.py
 __all__ = ["func", "some_var"]
 
 some_var = "data"
 some_other_var = "more data"
 
 def func():
return "hello"
 
 # -----------
 
 from some_module import *
 
 print(some_var)
 # "data"
 print(func())
 # "hello"
 
 print(some_other_var)
 # Exception, "some_other_var" is not exported by the module
登入後複製

这个属性可用于定义从模块导出哪些变量和函数。我们创建了一个Python模块…/some_module/单独文件(__init__.py)。在这个文件中定义了2个变量和一个函数,只导出其中的2个(func和some_var)。如果我们尝试在其他Python程序中导入some_module的内容,我们只能得到2个内容。

但是要注意,__all__变量只影响上面所示的* import,我们仍然可以使用显式的名称导入函数和变量,比如import some_other_var from some_module。

另一个常见的双下划线变量(模块属性)是__file__。这个变量标识了访问它的文件的路径:

 from pathlib import Path
 
 print(__file__)
 print(Path(__file__).resolve())
 # /home/.../directory/examples.py
 
 # Or the old way:
 import os
 print(os.path.dirname(os.path.abspath(__file__)))
 # /home/.../directory/
登入後複製

这样我们就可以结合__all__和__file__,可以在一个文件夹中加载所有模块:

# Directory structure:
 # .
 # |____some_dir
 # |____module_three.py
 # |____module_two.py
 # |____module_one.py
 
 from pathlib import Path, PurePath
 modules = list(Path(__file__).parent.glob("*.py"))
 print([PurePath(f).stem for f in modules if f.is_file() and not f.name == "__init__.py"])
 # ['module_one', 'module_two', 'module_three']
登入後複製

最后一个我重要的属性是的是__debug__。它可以用于调试,但更具体地说,它可以用于更好地控制断言:

 # example.py
 def func():
if __debug__:
print("debugging logs")
 
# Do stuff...
 
 func()
登入後複製

如果我们使用python example.py正常运行这段代码,我们将看到打印出“调试日志”,但是如果我们使用python -O example.py,优化标志(-O)将把__debug__设置为false并删除调试消息。因此,如果在生产环境中使用-O运行代码,就不必担心调试过程中被遗忘的打印调用,因为它们都不会显示。

创建自己魔法方法?

我们可以创建自己的方法和属性吗?是的,你可以,但你不应该这么做。

双下划线名称是为Python语言的未来扩展保留的,不应该用于自己的代码。如果你决定在你的代码中使用这样的名称,那么将来如果它们被添加到Python解释器中,这就与你的代码不兼容了。所以对于这些方法,我们只要记住和使用就好了。

以上是Python中的魔法方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

PHP和Python:代碼示例和比較 PHP和Python:代碼示例和比較 Apr 15, 2025 am 12:07 AM

PHP和Python各有優劣,選擇取決於項目需求和個人偏好。 1.PHP適合快速開發和維護大型Web應用。 2.Python在數據科學和機器學習領域佔據主導地位。

Python vs. JavaScript:社區,圖書館和資源 Python vs. JavaScript:社區,圖書館和資源 Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

CentOS上PyTorch的GPU支持情況如何 CentOS上PyTorch的GPU支持情況如何 Apr 14, 2025 pm 06:48 PM

在CentOS系統上啟用PyTorchGPU加速,需要安裝CUDA、cuDNN以及PyTorch的GPU版本。以下步驟將引導您完成這一過程:CUDA和cuDNN安裝確定CUDA版本兼容性:使用nvidia-smi命令查看您的NVIDIA顯卡支持的CUDA版本。例如,您的MX450顯卡可能支持CUDA11.1或更高版本。下載並安裝CUDAToolkit:訪問NVIDIACUDAToolkit官網,根據您顯卡支持的最高CUDA版本下載並安裝相應的版本。安裝cuDNN庫:前

docker原理詳解 docker原理詳解 Apr 14, 2025 pm 11:57 PM

Docker利用Linux內核特性,提供高效、隔離的應用運行環境。其工作原理如下:1. 鏡像作為只讀模板,包含運行應用所需的一切;2. 聯合文件系統(UnionFS)層疊多個文件系統,只存儲差異部分,節省空間並加快速度;3. 守護進程管理鏡像和容器,客戶端用於交互;4. Namespaces和cgroups實現容器隔離和資源限制;5. 多種網絡模式支持容器互聯。理解這些核心概念,才能更好地利用Docker。

minio安裝centos兼容性 minio安裝centos兼容性 Apr 14, 2025 pm 05:45 PM

MinIO對象存儲:CentOS系統下的高性能部署MinIO是一款基於Go語言開發的高性能、分佈式對象存儲系統,與AmazonS3兼容。它支持多種客戶端語言,包括Java、Python、JavaScript和Go。本文將簡要介紹MinIO在CentOS系統上的安裝和兼容性。 CentOS版本兼容性MinIO已在多個CentOS版本上得到驗證,包括但不限於:CentOS7.9:提供完整的安裝指南,涵蓋集群配置、環境準備、配置文件設置、磁盤分區以及MinI

CentOS上PyTorch的分佈式訓練如何操作 CentOS上PyTorch的分佈式訓練如何操作 Apr 14, 2025 pm 06:36 PM

在CentOS系統上進行PyTorch分佈式訓練,需要按照以下步驟操作:PyTorch安裝:前提是CentOS系統已安裝Python和pip。根據您的CUDA版本,從PyTorch官網獲取合適的安裝命令。對於僅需CPU的訓練,可以使用以下命令:pipinstalltorchtorchvisiontorchaudio如需GPU支持,請確保已安裝對應版本的CUDA和cuDNN,並使用相應的PyTorch版本進行安裝。分佈式環境配置:分佈式訓練通常需要多台機器或單機多GPU。所

CentOS上PyTorch版本怎麼選 CentOS上PyTorch版本怎麼選 Apr 14, 2025 pm 06:51 PM

在CentOS系統上安裝PyTorch,需要仔細選擇合適的版本,並考慮以下幾個關鍵因素:一、系統環境兼容性:操作系統:建議使用CentOS7或更高版本。 CUDA與cuDNN:PyTorch版本與CUDA版本密切相關。例如,PyTorch1.9.0需要CUDA11.1,而PyTorch2.0.1則需要CUDA11.3。 cuDNN版本也必須與CUDA版本匹配。選擇PyTorch版本前,務必確認已安裝兼容的CUDA和cuDNN版本。 Python版本:PyTorch官方支

CentOS上如何更新PyTorch到最新版本 CentOS上如何更新PyTorch到最新版本 Apr 14, 2025 pm 06:15 PM

在CentOS上更新PyTorch到最新版本,可以按照以下步驟進行:方法一:使用pip升級pip:首先確保你的pip是最新版本,因為舊版本的pip可能無法正確安裝最新版本的PyTorch。 pipinstall--upgradepip卸載舊版本的PyTorch(如果已安裝):pipuninstalltorchtorchvisiontorchaudio安裝最新

See all articles