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中文網其他相關文章!

Python在遊戲和GUI開發中表現出色。 1)遊戲開發使用Pygame,提供繪圖、音頻等功能,適合創建2D遊戲。 2)GUI開發可選擇Tkinter或PyQt,Tkinter簡單易用,PyQt功能豐富,適合專業開發。

Python适合数据科学、Web开发和自动化任务,而C 适用于系统编程、游戏开发和嵌入式系统。Python以简洁和强大的生态系统著称,C 则以高性能和底层控制能力闻名。

2小時內可以學會Python的基本編程概念和技能。 1.學習變量和數據類型,2.掌握控制流(條件語句和循環),3.理解函數的定義和使用,4.通過簡單示例和代碼片段快速上手Python編程。

Python在web開發、數據科學、機器學習、自動化和腳本編寫等領域有廣泛應用。 1)在web開發中,Django和Flask框架簡化了開發過程。 2)數據科學和機器學習領域,NumPy、Pandas、Scikit-learn和TensorFlow庫提供了強大支持。 3)自動化和腳本編寫方面,Python適用於自動化測試和系統管理等任務。

兩小時內可以學到Python的基礎知識。 1.學習變量和數據類型,2.掌握控制結構如if語句和循環,3.了解函數的定義和使用。這些將幫助你開始編寫簡單的Python程序。

如何在10小時內教計算機小白編程基礎?如果你只有10個小時來教計算機小白一些編程知識,你會選擇教些什麼�...

使用FiddlerEverywhere進行中間人讀取時如何避免被檢測到當你使用FiddlerEverywhere...

Python3.6環境下加載Pickle文件報錯:ModuleNotFoundError:Nomodulenamed...


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

禪工作室 13.0.1
強大的PHP整合開發環境

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

Dreamweaver CS6
視覺化網頁開發工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。