Python 中的魔法方法,也称为双下划线方法或特殊方法,格式为 “方法名”。这些方法无需主动调用,而是在特定场景下由 Python 解释器自动调用。魔法方法的作用是自定义类的行为,以便与内置操作符(例如 +、-、*、/、== 等)和函数(例如 len()、str() 等)交互。
魔法方法在不同的场景下被调用,例如:
__init__:在创建类的实例时初始化属性,是对象实例化时最常见的被调用的方法之一。__str__:当对象转换为字符串时被调用,如使用 str(obj) 转换为字符串或 print(obj) 面向用户输出对象等场景。目的是面向终端用户,提供用户友好的对象的字符串表示内容,不一定是对象完整精确描述,只对终端用户负责。如果类没有定义 __str__ 方法,Python 会试图把调用 __repr__ 的字符串结果返回给用户。__repr__:定义对象完整的、精确的、机器可读的字符串表示形式,如在 repr(obj) 方法调用,IDLE、Jupyter Notebook 等交互式环境中直接输入对象名时的输出内容等场景。面向 Python 内部,是对象合法的表达式字符串,通过该表达式字符串,可通过 eval() 函数执行并重新生成原始对象,即 eval(repr(obj)) == obj 应该为 True。如果类只定义了 __str__ 而没有定义 __repr__ 方法,Python 会试图调用 __str__ 方法来代替,但这通常不是一个好的做法。__add__:定义对象的加法行为,如两个自定义对象相加时被调用。__eq__:定义对象的相等性比较行为,用于判断两个对象是否相等。__len__:定义对象的长度,常用于自定义容器类,当使用 len() 函数对对象进行操作时被调用。这些魔法方法只是 Python 中的一部分,还有其他用于自定义对象行为的魔法方法,具体用法取决于我们的需求。使用魔法方法可以使我们的自定义类更具 Pythonic 和可读性。
__new__ 和 __init__ 在对象创建过程中起着至关重要的作用。__init__ 是我们很熟悉的初始化方法,在对象初始化的时候调用,通常被理解为 “构造函数”。实际上,当我们调用 x = SomeClass() 的时候,__init__ 并不是第一个执行的,__new__ 才是。
__new__ 是用来创建类并返回这个类的实例,而 __init__ 只是将传入的参数来初始化该实例。__new__ 在创建一个实例的过程中必定会被调用,但 __init__ 就不一定,比如通过 pickle.load 的方式反序列化一个实例时就不会调用 __init__。__new__ 方法总是需要返回该类的一个实例,而 __init__ 不能返回除了 None 的任何值。
例如:
class Foo(object):
def __init__(self):
print('foo __init__')
return None # 必须返回 None,否则抛 TypeError
def __del__(self):
print('foo __del__')在对象的生命周期结束时,__del__ 会被调用,可以将 __del__ 理解为 “析构函数”。__del__ 定义的是当一个对象进行垃圾回收时候的行为。有一点容易被人误解,实际上,x.__del__() 并不是对于 del x 的实现,但是往往执行 del x 时会调用 x.__del__()。
__str__ 和 __repr__ 在对象字符串表示中有不同的用途。__str__ 是一个对象的非正式的、易于阅读的字符串描述,当类 str 实例化(str(object))时会被调用,以及会被内置函数 format() 和 print() 调用。目的是面向终端用户,提供用户友好的对象的字符串表示内容,不一定是对象完整精确描述,只对终端用户负责。如果类没有定义 __str__ 方法,Python 会试图把调用 __repr__ 的字符串结果返回给用户。
例如:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Dog named {self.name} who is {self.age} years old"
my_dog = Dog("Buddy", 3)
print(my_dog)__repr__ 是一个对象的官方的字符串描述,会被内置函数 repr() 方法调用,它的描述必须是信息丰富的和明确的。面向 Python 内部,是对象合法的表达式字符串,通过该表达式字符串,可通过 eval() 函数执行并重新生成原始对象,即 eval(repr(obj)) == obj 应该为 True。如果类只定义了 __str__ 而没有定义 __repr__ 方法,Python 会试图调用 __str__ 方法来代替,但这通常不是一个好的做法。
例如:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f"Dog(name='{self.name}', age={self.age})"
my_dog = Dog("Buddy", 3)
print(repr(my_dog))__getattr__、__setattr__、__delattr__ 方法对对象属性的获取、设置和删除操作起着重要作用。
__getattr__(self, name) 定义了当用户试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向,或者对一些废弃的属性进行警告。__setattr__(self, name, value) 是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。但要注意实现 __setattr__ 时要避免 “无限递归” 的错误,正确的写法应该是 self.__dict__[name] = value。__delattr__(self, name) 与 __setattr__ 很像,只是它定义的是你删除属性时的行为。实现 __delattr__ 是同时要避免 “无限递归” 的错误。例如:
class Access(object):
def __getattr__(self, name):
print('__getattr__')
return super(Access, self).__getattr__(name)
def __setattr__(self, name, value):
print('__setattr__')
return super(Access, self).__setattr__(name, value)
def __delattr__(self, name):
print('__delattr__')
return super(Access, self).__delattr__(name)
access = Access()
access.attr1 = True # __setattr__调用
access.attr1 # 属性存在,只有__getattribute__调用
try:
access.attr2 # 属性不存在,先调用__getattribute__,后调用__getattr__
except AttributeError:
pass
del access.attr1 # __delattr__调用__getattribute__、__call__、__len__ 等方法在不同场景下有着重要的应用。
__getattribute__(self, name) 定义了你的属性被访问时的行为,相比较,__getattr__ 只有该属性不存在时才会起作用。因此,在支持 __getattribute__ 的 Python 版本,调用 __getattr__ 前必定会调用 __getattribute__。__getattribute__ 同样要避免 “无限递归” 的错误。需要提醒的是,最好不要尝试去实现 __getattribute__,因为很少见到这种做法,而且很容易出 bug。__call__ 在类中,允许创建可调用的对象 (实例)。就是说可以让函数(方法)可以像对象一样被调用。以 “对象名 ()” 的形式使用。__len__ 定义当被 len() 调用时的行为(返回容器中元素的个数)。例如:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __call__(self, *args, **kwargs):
return "<Point {}:{}>".format(self.x, self.y)
p = Point(4, 5)
print(p())
class MyList:
def __init__(self, values=None):
self.values = values or []
def __len__(self):
return len(self.values)
my_list = MyList([1, 2, 3])
print(len(my_list))在 Python 中,setup 方法主要用于构建和安装软件包。它提供了一种简单而灵活的方式来定义软件包的元数据、依赖关系等。
setup 方法通常位于 setup.py 文件中,通过使用 setuptools 模块的 setup 函数来实现。以下是一个简单的 setup.py 文件示例:
from setuptools import setup
setup(
name='mypackage',
version='1.0',
author='Your Name',
author_email='your@email.com',
description='A brief description of my package',
packages=['mypackage'],
install_requires=['dependency1', 'dependency2'],
)在这个示例中,name 参数指定了包的名称,version 参数指定了包的版本号,author 和 author_email 参数指定了包的作者信息,description 参数提供了包的简要描述。packages 参数指定了要安装的包的名称,这里假设 mypackage 是一个包含 __init__.py 文件的 Python 包。install_requires 参数列出了包的依赖关系,当安装这个包时,这些依赖项会自动被安装。
例如,如果我们的软件包依赖于 numpy 和 matplotlib,可以这样指定:
setup(
...
install_requires=['numpy', 'matplotlib'],
)这样,在安装我们的软件包时,如果用户的环境中没有安装 numpy 和 matplotlib,它们将会被自动安装。
除了基本用法外,setup 方法还提供了一些参数以满足更复杂的需求。
entry_points 参数:这个参数允许我们在安装软件包时创建可执行脚本或命令行工具。我们可以指定一个字典,其中键是工具的名称,值是要运行的函数或脚本。安装软件包后,这些工具将自动添加到系统的可执行路径中。例如,要创建一个名为 mytool 的工具,可以在 setup 函数中添加以下代码:
setup(
...
entry_points={'console_scripts': ['mytool=mypackage.tool:main',]},
)这将创建一个名为 mytool 的可执行文件,执行 mypackage.tool 模块中的 main 函数。
data_files 参数:这个参数允许我们将其他非 Python 文件包含在软件包中,例如配置文件、模板文件等。我们可以指定一个列表,其中每个元素表示一个文件或目录。安装软件包时,这些文件将被复制到指定的目标路径中。例如,要将一个名为 config.ini 的配置文件包含在软件包中,可以在 setup 函数中添加以下代码:
setup(
...
data_files=[('config', ['config.ini']),],
)这将把 config.ini 文件复制到软件包安装路径下的 config 目录中。
使用 setup 方法打包的软件包可以通过多种方式进行安装。
pip 安装:这是最常见的安装方式。确保你已经安装了 pip。然后,在命令行中进入软件包的根目录,并执行以下命令:
pip install .这将根据 setup.py 文件中的配置信息安装软件包及其依赖库。
如果应用在开发过程中会频繁变更,每次安装还需要先将原来的版本卸掉,很麻烦。使用 “develop” 开发方式安装的话,应用代码不会真的被拷贝到本地 Python 环境的 “site-packages” 目录下,而是在 “site-packages” 目录里创建一个指向当前应用位置的链接。这样如果当前位置的源码被改动,就会马上反映到 “site-packages” 里。
开发方式安装命令如下:
pip install -e .yum 安装(并非标准方式):通常情况下,Python 软件包不使用 yum 进行安装。但在某些特定的环境中,如果有特殊的需求,可以通过创建自定义的 yum 仓库或者使用其他工具将 Python 软件包集成到 yum 管理中。但这种方式比较复杂,且不是主流的安装方式。
总之,setup 方法在 Python 软件包的构建和安装中起着重要的作用,通过合理使用其各种参数和安装方式,可以方便地管理和分发 Python 代码。
Magic Method 和 Setup 方法在 Python 编程中虽然有着不同的功能,但也存在一些关联和相互作用。
Magic Method 主要用于自定义类的行为,使得对象能够与内置操作符和函数进行交互,从而实现更加灵活和强大的编程。例如,通过定义 __str__ 和 __repr__ 方法,可以控制对象的字符串表示形式,方便在调试和输出时提供更有意义的信息。通过定义 __add__、__eq__ 等方法,可以实现自定义对象的运算和比较操作。
Setup 方法则主要用于构建和安装软件包,定义软件包的元数据、依赖关系以及安装方式等。它为开发者提供了一种方便的方式来组织和分发代码。
在某些情况下,Magic Method 可以与 Setup 方法结合使用。例如,在构建一个自定义的数据结构类时,可以使用 Magic Method 来定义该类的特殊行为,如 __len__ 方法用于返回数据结构的长度。如果这个数据结构类被打包成一个软件包,那么 Setup 方法可以用来安装这个软件包,并确保其依赖关系得到满足。
另外,当使用 Setup 方法创建可执行脚本或命令行工具时,可以利用 Magic Method 来定义这些工具的行为。例如,可以定义 __call__ 方法,使得工具对象可以像函数一样被调用。
总之,Magic Method 和 Setup 方法在 Python 编程中各有其独特的作用,但它们也可以相互结合,为开发者提供更全面的编程解决方案。了解它们之间的关联和相互作用,可以帮助开发者更好地利用 Python 的强大功能,提高开发效率。