Python中的魔法属性

小詹学Python

共 14372字,需浏览 29分钟

 ·

2021-04-10 12:03

魔法属性

在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(1010)

In [34]: r.x, r.y
Out[34]: (1010)

In [35]: r(00)

In [36]: r.x, r.y
Out[36]: (00)

In [37]: r(100100)

In [38]: r.x, r.y
Out[38]: (100100)

__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-1None)
Out[72]: []

In [73]: mlist[:] = [1,2,3]
__getitem__() called
slice(NoneNoneNone) [123]

In [74]: mlist[:]
__getitem__() called
slice(NoneNoneNone)
Out[74]: [123]

In [75]: mlist[0:2]
__getitem__() called
slice(02None)
Out[75]: [12]

In [76]: mlist[::-1]
__getitem__() called
slice(NoneNone-1)
Out[76]: [321]

In [77]: mlist[0]
__getitem__() called
0

In [78]: mlist[0:1]
__getitem__() called
slice(01None)
Out[78]: [1]

In [79]: del mlist[0:1]
__delitem__() called

In [80]: mlist[:]
__getitem__() called
slice(NoneNoneNone)
Out[80]: [23]

注意: 当进行 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的地位。我们不是键盘侠,我们只是平凡世界中不凡的缔造者 。

推荐阅读
误执行了rm -fr /*之后,除了跑路还能怎么办?!
程序员必备58个网站汇总
大幅提高生产力:你需要了解的十大Jupyter Lab插件

浏览 11
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报