当前位置:

首页 > 编程开发 > Python中常用的魔法方法有哪些?

Python中常用的魔法方法有哪些?

python中的魔法方法是一些可以让你对类添加“魔法”的特殊方法,它们经常是两个下划线包围来命名的。Python的魔法方法,也称为dunder(双下划线)方法。大多数的时候,我们将它们用于简单的事情,例如构造函数(init)、字符串表示(str,repr)或算术运算符(add/mul)。其实还有许多你可能没有听说过的但是却很好用的方法,在这篇文章中,我们将整理这些魔法方法!迭代器的大小我们都知道__len__方法,可以用它在容器类上实现len()函数。但是,如果您想获取实现迭代器的类对象的长度怎么办?i

本文目录

    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 larger or smaller than 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 raised).

    元编程

    大部分很少看到的神奇方法都与元编程有关,虽然元编程可能不是我们每天都需要使用的东西,但有一些方便的技巧可以使用它。

    一个这样的技巧是使用__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解释器中,这就与你的代码不兼容了。所以对于这些方法,我们只要记住和使用就好了。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Python 运算符
    相关文章 更多
    python删除一个列表里的重复元素的方法与代码示例
    python删除一个列表里的重复元素的方法与代码示例

    学习如何使用Python高效删除列表中的重复元素。本文提供5种常见方法的代码示例,涵盖保持顺序与不保持顺序的场景,适合初学者和进阶开发者参考。

    Python写接口的步骤及完整代码示例
    Python写接口的步骤及完整代码示例

    详细讲解使用Python编写Web接口的核心步骤与完整代码示例,涵盖环境准备、路由定义、数据校验、服务启动及调试测试方法。

    codex怎么读取word文档及docx文件操作教程
    codex怎么读取word文档及docx文件操作教程

    介绍如何利用编程工具读取和解析Word文档(docx格式),涵盖基础文本提取代码示例、结构化数据处理逻辑,以及处理复杂排版时的技术局限与替代方案。

    怎么在Python中调用C语言代码提升性能
    怎么在Python中调用C语言代码提升性能

    详细讲解在Python中调用C语言代码提升性能的完整实践指南,包含原理分析、编译步骤、ctypes与CFFI具体实现、性能对比及常见陷阱。

    Python与C语言的性能差异及底层原理
    Python与C语言的性能差异及底层原理

    深入解析Python与C语言在执行效率上的数量级差异,从编译机制、变量内存布局、动态类型推断到GIL锁,系统阐明底层核心原理。

    Python和C语言哪个更适合编程入门
    Python和C语言哪个更适合编程入门

    客观对比Python与C语言在心智负担、底层机制与学习反馈上的核心差异,提供清晰有据的选择标准与切实的入门学习路径。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    langgraph python guide中文入门教程详解
    langgraph python guide中文入门教程详解

    本篇LangGraph Python中文入门教程详解,系统梳理StateGraph状态机定义、节点编写、边与条件路由、检查点持久化(MemorySaver)等核心机制,提供从环境安装到完整循环智能体构建的代码示例与实操指导。

    Windows 安装 Python:从官方入口到虚拟环境的可复现实践
    Windows 安装 Python:从官方入口到虚拟环境的可复现实践

    面向后端开发者,基于 Python 官方文档说明如何在 Windows 上安装 Python、创建 venv 虚拟环境及使用 pip 管理依赖。包含可复制命令、验证方法及工具选择边界,避免常见配置误区。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。