Python类-magic methods魔术方法, since
(2022.04.19-20 Tues-Wed)
Python中的魔术方法,是可以定义在类中的特殊方法,可在特定的情况下调用。魔术方法以双下划线(underscore)包裹,形式如__call__
, __new__
等。本文整理常用的magic method和对应的功能,并给出实例。
magic methods列表
magic method的功能大致分为如下几类:
- 构造与初始化
- 运算符
- 类型转换
- 类表达
- 控制属性access
- 调用(callable objects)
- 赋值
magic method | description |
---|---|
__init__(self, [...) |
初始化器,接收参数 |
__new__(cls, [...) |
构造函数-创建类、实例化、返回类的实例 |
__del__(self) |
析构函数-对象进行垃圾收回时调用 |
__cmp__(self, other) |
比较运算符,分别返回1,0,-1对于self>other, self=other和self=other的情况 |
__eq__(self, other) |
== equality operator的行为 |
__ne__(self, other) |
!= |
__lt__(self, other) |
< |
__gt__(self, other) |
> |
__le__(self, other) |
<= |
__ge__(self, other) |
>= |
__pos__(self) |
+some_object |
__ne__(self) |
-some_object |
__abs__(self) |
abs() |
__invert__(self) |
invert运算符,反转 |
__round__(self) |
round() |
__floor__(self) |
math.floor() |
__ceil__(self) |
math.ceil() |
__trunc__(self) |
math.trunc() |
__add/radd__(self, other) |
加法/反加操作 reflected addition |
__sub/rsub__(self, other) |
减法/反减 |
__mul/rmul__(self, other) |
乘/反乘 |
__floordiv/rfloordiv__(self, other) |
// |
__div/rdiv__(self, other) |
/ |
__mod/rmod__(self, other) |
% |
__pow/rpow__ |
** |
__and/or/xor__(self, other) |
& / | / ^
|
__iadd__(self, other) |
自加,其他运算符以此类推 |
__int__(self) |
类型转换为int,其他的包括float, long, complex, oct, hex |
__str__(self) |
返回str,当前实例文本显示的内容,即print(this_ins),针对人显示 |
__repr__(self) |
直接调用当前实例的显示内容,针对机器显示,>> this_ins 返回的内容 |
__class__ |
查看当前实例所属的类,不传递参数 |
__format__(self, formatstr) |
"Hello, {0:abc}!".format(a) would lead to the call a.__format__("abc") |
__getattr__(self, attr) |
定义访问不存在的属性attr的返回结果 |
__getattribute__(self, attr) |
定义访问一个存在的属性attr的返回结果 |
__setattr__(self, attr, value) |
定义设定一个属性attr的值成为value的时候的操作 |
__delattr__(self, attr) |
定义执行del attr 的操作 |
__call__(self, para) |
像调用函数一样调用一个实例 |
__getitem__(self, key) |
用key作为index访问的返回值,self[key] |
__setitem__(self, key, value) |
设定index为key的元素值为value,self[key] = value |
__delitem__(self, key) |
删除index为key的元素 |
__annotations__ |
查看函数的注释 |
其他magic method包括可迭代对象的__iter__
, __next__
,上下文管理的__enter__
, __exit__
,类注释的__doc__
,__slot__
, __dict__
,定义len()
函数的方法__len__
等。
说明和举例:
-
__init__
和__new__
的差别:
这两个方法都在类的实例化时被创建。__new__
被首先调用,__init__
其次。__new__
返回新的类实例,__init__
为新的实例设置属性,无返回值。
class A(object):
def __new__(cls):
print("New method")
return super(A, cls).__new__(cls)
def __init__(self):
print("Init method ")
>>> A()
New method
Init method
<__main__.A object at 0x1016a2b80>
(2022.04.28 Thu)
__new__
覆盖初始类的__new__
方法,用于实现工厂模式、单例模式、元编程(需要用__new__
控制对象创建)。下面给出工厂模式的一个例子。
class Shape(object):
def __init__(self):
pass
def draw(self):
pass
class Rectangle(Shape):
def __init__(self):
print('This is a rectangle.')
def draw(self):
print('drawing a rectangle')
class Square(Shape):
def __init__(self):
print('This is a square.')
def draw(self):
print('drawing a square.')
class ShapeFactory(object):
shapes = {'rectangle': Rectangle, 'square': Square}
def __new__(cls, name):
if name in ShapeFactory.shapes.keys():
print('creating a new shape: {}'.format(name))
return ShapeFactory.shapes[name]() #返回实例化的对象
else:
print('creating a shape')
return Shape()
调用类ShapeFactory
>>> a = ShapeFactory('rectangle') # 这里的a是已经经过实例化的对象,不需要再加括号调用
creating a new shape: rectangle
This is a rectangle.
>>> a.draw()
drawing a rectangle
-
__init__()
和__init__.py
的区别
(2022.11.04 Fri)
上面已经介绍到__init__
方法用于为新的类实例设置属性,而一个 文件夹中的__init__.py
文件则有完全不同的作用。
Python的模块(Module)指的是一个单独的Python文件,而包(package)指的是一个文件夹,该文件夹是模块的集合。
当用户使用import
指令从一个文件夹引入module/package时,如果该文件夹本身含有__init__.py
文件,则从该文件夹的目录下引入文件时,编译器将该文件夹当做package对待。
即__init__.py
文件用于将Python的包package初始化。
通常__init__.py
文件为空,但可以为它增加功能。在导入一个包时,实际上是导入了该包所在文件夹内的__init__.py
文件。这样我们可以在__init__.py
文件中批量导入所需模块,而不再需要逐一导入。
当使用import
指令因为文件夹时,会首先执行该文件夹下的__init__.py
文件。如果该文件中也有导入命令,则执行。当用户使用from xxx import *
这个指令时,会导入该文件夹内的所有module。如果控制该命令导出的内容,可在__init__.py
文件中加入__all__=[xxx]
的命令,用以限制*
导出的方法。具体内容参考本文的__all__
部分。
-
__getattr__
,__getattribute__
的差别
__getattr__
用于定义查询不存在的属性时的操作,而__getattribute__
定义了查询存在的属性时的操作。默认情况下访问不存在的属性返回AttributeError: xx object has no attribute xx
.
注意当访问属性时,首先调用__getattribute__
方法,如果访问的属性在实例中不存在,则再调用__getattr__
方法。如下面案例所示
class attrc:
var = 0
def __getattr__(self, attr):
print('get a non-existing attribute')
return '1'
def __getattribute__(self, attr):
print('get an existing attribute')
return super().__getattribute__(attr)
调用
>>> m = attrc()
>>> m.var
get an existing attribute
0
>>> m.k #m中不含k这个属性
get an existing attribute
get a non-existing attribute
'1'
__call__
class cs:
def __call__(self, val=None):
if val:
print('value = ', val)
return 'call return: 1'
调用实例
>>> m = cs()
>>> m()
'call return: 1'
>>> m(99)
value = 99
'call return: 1'
-
__add__
和继承list的案例
设计一个新的数据类型,特征和list完全一致,除了加法操作由list的拼接改成对应元素相加。
class listLike(list):
def __add__(self, al):
if isinstance(al, list) or isinstance(al, tuple):
pass
else:
return 'TypeError: add a list or tuple'
if len(al) < len(self):
return 'LengthError: specify a longer sequence'
return [self[i]+al[i] for i in range(len(self))]
def __radd__(self, al):
return self.__add__(al)
调用
>>> a = listLike([1,2,3])
>>> a+[4,5,6]
[5, 7, 9]
>>> a
[1, 2, 3]
>>> [4,5,6]+ a
[5, 7, 9]
-
__annotations__
查看函数注释
(2022.05.27 Fri)
Python 3.0加入了函数注释功能,比如下面例子中的func
函数,输入的两个参数分别是a1
和a2
类型分别是str
和int
,默认值为1
,函数返回类型为int
。注意赋默认值的变量放在后面。
def func(a1:str, a2:int=1) ->int:
"""
this is function help info
"""
return 1
此时查看该函数的内置magic method/method decorated by @property
__annotations__
,会看到返回的字典其中包含了函数的输入输出类型。
>> func.__annotations__
{'a1': <class 'str'>, 'a2': <class 'int'>, 'return': <class 'int'>}
另外,可以通过help
函数查看该函数在定义之后的解释信息
>> help(func)
Help on function func in module __main__:
func(a1: str, a2: int = 1) -> int
this is function help info
查看help信息结束,按q
退出。
(2022.11.02 Wed)
-
__all__
当使用import
和from
指令(from xxx import *
)从模块中导入方法/子模块时,导入的属性、方法、子类的特点是不以单下划线_
开头的对象。
比如如下的Python文件
# test__all__.py
def func1():
print(1)
def func2():
print(2)
def _func3():
print(3)
导入之后,查看内存中的对象
>> from test__all__ import *
>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__',
'__name__', '__package__', '__spec__', 'func1', 'func2']
没有下划线开头的对象func1
和func2
被导入内存,以下划线开头的对象_func3
没有被导入内存。
除此之外,可在test__all__
模块中加入__all__
变量,其值为列表,存储的是当前模块成员(属性、方法、子类)的名称。在模块中设置了__all__
变量,当代码中通过from xxx import *
导入模块时,只有__all__
变量中指定的成员会被导入,即便该成员的名字是以下划线_
开头。
# test__all__.py
def func1():
print(1)
def func2():
print(2)
def _func3():
print(3)
__all__ = ['func1', '_func3']
导入
>> from test__all__ import *
>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__',
'__name__', '__package__', '__spec__', '_func3', 'func1']
变量__all__
的设置是针对from xxx import *
的命令,一旦采用其他方式导入,__all__
的设置将失效,分别是import xxx
和from xxx import func2, _func3
>> import test__all__ as ta
>> ta.func2()
2
>> from test__all__ import func2, _func3
>> func2()
2
>> _func3()
3
Reference
1 rszalski点github点io/magicmethods/