Python中的魔法属性
魔法属性
在Python中,所有以
__
双下划线包起来的方法,都统称为Magic Method
,例如类的初始化方法__init__()
,实例对象创造方法__new__()
等。魔法属性和方法是Python内置的一些属性和方法,有着特殊的含义。命名时前后加上两个下划线,在执行系统特定操作时,会自动调用。
常见的魔法属性
__doc__
表示类的描述信息
# __doc__
class Foo:
""" 描述类信息,这是用于测试的类 """
def func(self):
pass
# ipython 测验
In [2]: Foo.__doc__
Out[2]: ' 描述类信息,这是用于测试的类 '
__module__ 和 __class__
__module__
表示当前操作的对象在那个模块__class__
表示当前操作的对象的类是什么
# __module__、__class__
# oop.py
class Student(object):
def __init__(self, name):
self.name = name
# main.py
from oop import Student
s = Student()
print(s.__module__) # 输出 oop 即:输出模块
print(s.__class__) # 输出 <class 'oop.Student'> 即:输出类
__init__ 、__new__
__init__()
初始化方法 和__new__()
,通过类创建对象时,自动触发执行。__new__
是用来创建类并返回这个类的实例,而__init__
只是将传入的参数来初始化该实例。
__new__()
创建对象时调用,会返回当前对象的一个实例__init__()
创建完对象后调用,对当前对象的一些实例初始化,无返回值
# __init__ 、 __new__
class Student(object):
def __init__(self, name, age):
print('__init__() called')
self.name = name
self.age = age
def __new__(cls, *args, **kwargs):
print('__new__() called')
print(cls, args, kwargs)
return super().__new__(cls)
# ipython 测验
In [26]: s1 = Student('hui', age=21)
__new__() called
<class '__main__.Student'> ('hui',) {'age': 21}
__init__() called
In [27]: s2 = Student('jack', age=20)
__new__() called
<class '__main__.Student'> ('jack',) {'age': 20}
__init__() called
__del__
当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,有 内存管理、垃圾回收机制,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,__del__
的调用是由解释器在进行垃圾回收时自动触发执行的。
# __del__
class Foo:
def __del__(self):
print('__del__() called')
# ipython 测验
In [29]: f = Foo()
In [30]: del f
__del__() called
__call__
让类的实例的行为表现的像函数一样,你可以调用它们,将一个函数当做一个参数传到另外一个函数中等等。这是一个非常强大的特性,其让Python编程更加舒适甜美。对象后面加括号,触发执行。
注:__init__
方法的执行是由创建对象触发的,即:对象 = 类名()
;而对于 __call__
方法的执行是由对象后加括号触发的,即:对象()
或者 类()()
__call__
在那些 类的实例经常改变状态的时候会非常有效。调用这个实例是一种改变这个对象状态的直接和优雅的做法。用一个实例来表达最好不过了:
# __call__
class Rect(object)
"""
调用实例对象来改变矩形的位置
"""
def __init__(self, x, y):
# x, y代表矩形坐标
self.x, self.y = x, y
def __call__(self, x, y):
# 改变实体的位置
self.x, self.y = x, y
# ipython 测验
In [33]: r = Rect(10, 10)
In [34]: r.x, r.y
Out[34]: (10, 10)
In [35]: r(0, 0)
In [36]: r.x, r.y
Out[36]: (0, 0)
In [37]: r(100, 100)
In [38]: r.x, r.y
Out[38]: (100, 100)
__dict__
类或对象中的所有属性
类的实例属性属于对象;类中的类属性和方法等属于类,即:
# __dict__
class Student(object):
def __init__(self, name, age):
self.name = name
self._age = age
@property
def age(self):
return self._age
# ipython 测验
In [47]: # 获取类属性
In [48]: Student.__dict__
Out[48]:
mappingproxy({'__module__': '__main__',
'__init__': <function __main__.Student.__init__(self, name, age)>,
'age': <property at 0x210e2a005e8>,
'__dict__': <attribute '__dict__' of 'Student' objects>,
'__weakref__': <attribute '__weakref__' of 'Student' objects>,
'__doc__': None})
In [49]: # 获取实例对象的属性
In [50]: s = Student('hui', 21)
In [51]: s.__dict__
Out[51]: {'name': 'hui', '_age': 21}
In [52]: s2 = Student('jack', 20)
In [53]: s2.__dict__
Out[53]: {'name': 'jack', '_age': 20}
__str__
如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。
In [65]: # __str__
...: class Foo(object):
...: pass
...:
In [66]: f = Foo()
In [67]: print(f)
<__main__.Foo object at 0x00000210E2715608>
In [68]: class Foo(object):
...:
...: def __str__(self):
...: return '< Custom Foo object str >'
...:
In [69]: f = Foo()
In [70]: print(f)
< Custom Foo object str >
__getitem__、__setitem__、__delitem__
用于索引操作,如字典。以上分别表示获取、设置、删除数据。
用于切片操作,如列表。
字典示例
# __getitem__、__setitem__、__delitem__
class MyDict(object):
def __init__(self):
self.my_dict = dict()
def __getitem__(self, key):
print('__getitem__() ', key)
return self.my_dict.get(key, None)
def __setitem__(self, key, value):
print('__setitem__() ', key, value)
self.my_dict.update(key=value)
def __delitem__(self, key):
print('__delitem__() ', key)
del self.my_dict[key]
# ipython 测验
In [33]: mdict = MyDict()
In [34]: print(mdict['name'])
__getitem__() name
None
In [35]: # 新增
In [36]: mdict['name'] = 'hui'
__setitem__() name hui
In [37]: mdict['age'] = 21
__setitem__() age 21
In [38]: mdict['name']
__getitem__() name
Out[38]: 'hui'
In [39]: mdict['age']
__getitem__() age
Out[39]: 21
In [40]: # 更新
In [41]: mdict['name'] = 'jack'
__setitem__() name jack
In [42]: mdict['name']
__getitem__() name
Out[42]: 'jack'
In [43]: # 删除
In [44]: del mdict['age']
__delitem__() age
In [45]: print(mdict['age'])
__getitem__() age
None
列表示例
# 切片操作
class MyList(object):
def __init__(self):
self.mlist = list()
def __getitem__(self, index):
print('__getitem__() called')
print(index)
if isinstance(index, slice):
return self.mlist[index]
def __setitem__(self, index, value):
print('__getitem__() called')
print(index, value)
if isinstance(index, slice):
self.mlist[index] = value
def __delitem__(self, index):
print('__delitem__() called')
if isinstance(index, slice):
del self.mlist[index]
# ipython 测验
In [70]: mlist = MyList()
In [71]: mlist[0]
__getitem__() called
0
In [72]: mlist[0:-1]
__getitem__() called
slice(0, -1, None)
Out[72]: []
In [73]: mlist[:] = [1,2,3]
__getitem__() called
slice(None, None, None) [1, 2, 3]
In [74]: mlist[:]
__getitem__() called
slice(None, None, None)
Out[74]: [1, 2, 3]
In [75]: mlist[0:2]
__getitem__() called
slice(0, 2, None)
Out[75]: [1, 2]
In [76]: mlist[::-1]
__getitem__() called
slice(None, None, -1)
Out[76]: [3, 2, 1]
In [77]: mlist[0]
__getitem__() called
0
In [78]: mlist[0:1]
__getitem__() called
slice(0, 1, None)
Out[78]: [1]
In [79]: del mlist[0:1]
__delitem__() called
In [80]: mlist[:]
__getitem__() called
slice(None, None, None)
Out[80]: [2, 3]
注意: 当进行 mlist[0]
操作的时候传递并不是一个 slice
对象,不是一个 int
类型的数字,所以不能把索引为 0
的值取出来,改成 mlist[0, 1]
或者在 __getitem__()
的方法中新增数字判断,大家可以尝试一下。
__enter__、__exit__
with
声明是从 Python2.5
开始引进的关键词。你应该遇过这样子的代码:
with open('foo.txt') as bar:
# do something with bar
pass
在 with
声明的代码段中,我们可以做一些对象的开始操作和退出操作,还能对异常进行处理。这需要实现两个魔术方法: __enter__ 和 __exit__。
__enter__(self):
定义了当使用 with
语句的时候,会话管理器在块被初始创建时要产生的行为。请注意,__enter__
的返回值与 with
语句的目标或者 as
后的名字绑定。
__exit__(self, exception_type, exception_value, traceback):
定义了当一个代码块被执行或者终止后,会话管理器应该做什么。它可以被用来处理异常、执行清理工作或做一些代码块执行完毕之后的日常工作。如果代码块执行成功,exception_type,exception_value,和traceback
将会为 None
。否则,你可以选择处理这个异常或者是直接交给用户处理。如果你想处理这个异常的话,请确保__exit__
在所有语句结束之后返回 True
。如果你想让异常被会话管理器处理的话,那么就让其产生该异常。
__copy__、__deepcopy__
有时候,尤其是当你在处理可变对象时,你可能想要复制一个对象,然后对其做出一些改变而不希望影响原来的对象。这就是Python的copy所发挥作用的地方。
__copy__(self):
定义了当对你的类的实例调用 copy.copy()
时所产生的行为。copy.copy()
返回了你的对象的一个浅拷贝——这意味着,当实例本身是一个新实例时,它的所有数据都被引用了——例如,当一个对象本身被复制了,它的数据仍然是被引用的(因此,对于浅拷贝中数据的更改仍然可能导致数据在原始对象的中的改变)。
__deepcopy__(self, memodict={}):
定义了当对你的类的实例调用 copy.deepcopy()
时所产生的行为。copy.deepcopy()
返回了你的对象的一个深拷贝——对象和其数据都被拷贝了。memodict
是对之前被拷贝的对象的一个缓存——这优化了拷贝过程并且阻止了对递归数据结构拷贝时的无限递归。当你想要进行对一个单独的属性进行深拷贝时,调用copy.deepcopy()
,并以 memodict
为第一个参数。
这些魔术方法的用例看起来很小,并且确实非常实用. 它们反应了关于面向对象程序上一些重要的东西在Python
上,并且总的来说 Python
总是一个简单的方法去找某些事情,即使是没有必要的。这些魔法方法可能看起来不是很有用,但是一旦你需要它们,你会感到庆幸它们的存在。
其他魔法方法
由于魔法属性、方法太多了在这就不一一描述和展示了,其他的就以表格形式呈现吧。
用于比较的魔术方法
方法 | 作用 |
---|---|
__cmp__(self, other) | 比较方法里面最基本的的魔法方法 |
__eq__(self, other) | 定义相等符号的行为,== |
__ne__(self,other) | 定义不等符号的行为,!= |
__lt__(self,other) | 定义小于符号的行为,< |
__gt__(self,other) | 定义大于符号的行为,> |
__le__(self,other) | 定义小于等于符号的行为,<= |
__ge__(self,other) | 定义大于等于符号的行为,>= |
数值计算的魔术方法
单目运算符和函数
方法 | 作用 |
---|---|
__pos__(self) | 实现一个取正数的操作 |
__neg__(self) | 实现一个取负数的操作 |
__abs__(self) | 实现一个内建的 abs() 函数的行为 |
__invert__(self) | 实现一个取反操作符(~操作符)的行为 |
__round__(self, n) | 实现一个内建的 round() 函数的行为 |
__floor__(self) | 实现 math.floor() 的函数行为 |
__ceil__(self) | 实现 math.ceil() 的函数行为 |
__trunc__(self) | 实现 math.trunc() 的函数行为 |
双目运算符或函数
方法 | 作用 |
---|---|
__add__(self, other) | 实现一个加法 |
__sub__(self, other) | 实现一个减法 |
__mul__(self, other) | 实现一个乘法 |
__floordiv__(self, other) | 实现一个 // 操作符产生的整除操作 |
__div__(self, other) | 实现一个 / 操作符代表的除法操作 |
__truediv__(self, other) | 实现真实除法 |
__mod__(self, other) | 实现一个 % 操作符代表的取模操作 |
__divmod__(self, other) | 实现一个内建函数 divmod() |
__pow__(self, other) | 实现一个指数操作( ****** 操作符)的行为 |
__lshift__(self, other) | 实现一个位左移操作**(<<)**的功能 |
__rshift__(self, other) | 实现一个位右移操作**(>>)**的功能 |
__and__(self, other) | 实现一个按位进行与操作**(&)**的行为 |
__or__(self, other) | 实现一个按位进行或操作的行为 |
__xor__(self, other) | 异或运算符相当于 ^ |
增量运算
方法 | 作用 |
---|---|
__iadd__(self, other) | 加法赋值 |
__isub__(self, other) | 减法赋值 |
__imul__(self, other) | 乘法赋值 |
__ifloordiv__(self, other) | 整除赋值,地板除,相当于 //= 运算符 |
__idiv__(self, other) | 除法赋值,相当于 /= 运算符 |
__itruediv__(self, other) | 真除赋值 |
__imod_(self, other) | 模赋值,相当于 %= 运算符 |
__ipow__(self, other) | 乘方赋值,相当于 **= 运算符 |
__ilshift__(self, other) | 左移赋值,相当于 <<= 运算符 |
__irshift__(self, other) | 左移赋值,相当于 >>= 运算符 |
__iand__(self, other) | 与赋值,相当于 &= 运算符 |
__ior__(self, other) | 或赋值 |
__ixor__(self, other) | 异或运算符,相当于 ^= 运算符 |
类型转换
方法 | 作用 |
---|---|
__int__(self) | 转换成整型 |
__long__(self) | 转换成长整型 |
__float__(self) | 转换成浮点型 |
__complex__(self) | 转换成 复数型 |
__oct__(self) | 转换成八进制 |
__hex__(self) | 转换成十六进制 |
__index__(self) | 如果你定义了一个可能被用来做切片操作的数值型,你就应该定义__index__ |
__trunc__(self) | 当 math.trunc(self) 使用时被调用 __trunc__ 返回自身类型的整型截取 |
__coerce__(self, other) | 执行混合类型的运算 |
大自然用数百亿年创造出我们现实世界,而程序员用几百年创造出一个完全不同的虚拟世界。我们用键盘敲出一砖一瓦,用大脑构建一切。人们把1000视为权威,我们反其道行之,捍卫1024的地位。我们不是键盘侠,我们只是平凡世界中不凡的缔造者 。