Python中的魔法属性

简介: 在Python中,所有以 `__` 双下划线包起来的方法,都统称为 `Magic Method`,例如类的初始化方法 `__init__()` ,实例对象创造方法 `__new__()`等。魔法属性和方法是Python内置的一些属性和方法,有着特殊的含义。命名时前后加上两个下划线,在执行系统特定操作时,会自动调用。

魔法属性

在Python中,所有以 __ 双下划线包起来的方法,都统称为 Magic Method,例如类的初始化方法 __init__() ,实例对象创造方法 __new__()等。

魔法属性和方法是Python内置的一些属性和方法,有着特殊的含义。命名时前后加上两个下划线,在执行系统特定操作时,会自动调用。

<br/>

常见的魔法属性

\_\_doc\_\_

表示类的描述信息

# __doc__
class Foo:
    """ 描述类信息,这是用于测试的类 """
    
    def func(self):
        pass

    
# ipython 测验
In [2]: Foo.__doc__
Out[2]: ' 描述类信息,这是用于测试的类 '
    

<br/>

\_\_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'> 即:输出类

<br/>

\_\_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

<br/>

\_\_del\_\_

当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,有 内存管理、垃圾回收机制,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,__del__ 的调用是由解释器在进行垃圾回收时自动触发执行的。

# __del__
class Foo:
    def __del__(self):
        print('__del__() called')

        
# ipython 测验
In [29]: f = Foo()

In [30]: del f
__del__() called

<br/>

\_\_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)

<br/>

\_\_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}

<br/>

\_\_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 >

<br/>

\_\_getitem\_\_、\_\_setitem\_\_、\_\_delitem\_\_

用于索引操作,如字典。以上分别表示获取、设置、删除数据。

用于切片操作,如列表。

<br/>

字典示例

# __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

<br/>

列表示例

# 切片操作
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]

<br/>

注意: 当进行 mlist[0] 操作的时候传递并不是一个 slice 对象,不是一个 int 类型的数字,所以不能把索引为 0 的值取出来,改成 mlist[0, 1] 或者在 __getitem__() 的方法中新增数字判断,大家可以尝试一下。

<br/>

\_\_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。如果你想让异常被会话管理器处理的话,那么就让其产生该异常。

<br/>

\_\_copy\_\_、\_\_deepcopy\_\_

  有时候,尤其是当你在处理可变对象时,你可能想要复制一个对象,然后对其做出一些改变而不希望影响原来的对象。这就是Python的copy所发挥作用的地方。

__copy__(self):

  定义了当对你的类的实例调用 copy.copy() 时所产生的行为。copy.copy() 返回了你的对象的一个浅拷贝——这意味着,当实例本身是一个新实例时,它的所有数据都被引用了——例如,当一个对象本身被复制了,它的数据仍然是被引用的(因此,对于浅拷贝中数据的更改仍然可能导致数据在原始对象的中的改变)。

__deepcopy__(self, memodict={}):

  定义了当对你的类的实例调用 copy.deepcopy()时所产生的行为。copy.deepcopy() 返回了你的对象的一个深拷贝——对象和其数据都被拷贝了。memodict 是对之前被拷贝的对象的一个缓存——这优化了拷贝过程并且阻止了对递归数据结构拷贝时的无限递归。当你想要进行对一个单独的属性进行深拷贝时,调用copy.deepcopy(),并以 memodict 为第一个参数。

<br/>

  这些魔术方法的用例看起来很小,并且确实非常实用. 它们反应了关于面向对象程序上一些重要的东西在Python 上,并且总的来说 Python 总是一个简单的方法去找某些事情,即使是没有必要的。这些魔法方法可能看起来不是很有用,但是一旦你需要它们,你会感到庆幸它们的存在。

<br/>

其他魔法方法

由于魔法属性、方法太多了在这就不一一描述和展示了,其他的就以表格形式呈现吧。

用于比较的魔术方法

方法 作用
__cmp__(self, other) 比较方法里面最基本的的魔法方法
__eq__(self, other) 定义相等符号的行为,==
__ne__(self,other) 定义不等符号的行为,!=
__lt__(self,other) 定义小于符号的行为,<
__gt__(self,other) 定义大于符号的行为,>
__le__(self,other) 定义小于等于符号的行为,<=
__ge__(self,other) 定义大于等于符号的行为,>=

<br/>

数值计算的魔术方法

单目运算符和函数

方法 作用
__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() 的函数行为

<br/>

双目运算符或函数

方法 作用
__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) 异或运算符相当于 ^

<br/>

增量运算

方法 作用
__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) 异或运算符,相当于 ^= 运算符

<br/>

类型转换

方法 作用
__int__(self) 转换成整型
__long__(self) 转换成长整型
__float__(self) 转换成浮点型
__complex__(self) 转换成 复数型
__oct__(self) 转换成八进制
__hex__(self) 转换成十六进制
__index__(self) 如果你定义了一个可能被用来做切片操作的数值型,你就应该定义__index__
__trunc__(self) math.trunc(self) 使用时被调用 __trunc__ 返回自身类型的整型截取
__coerce__(self, other) 执行混合类型的运算

<br/>

源代码

源代码已上传到 Gitee PythonKnowledge: Python知识宝库,欢迎大家来访。

✍ 码字不易,还望各位大侠多多支持❤️。

<br/>

公众号

新建文件夹X

大自然用数百亿年创造出我们现实世界,而程序员用几百年创造出一个完全不同的虚拟世界。我们用键盘敲出一砖一瓦,用大脑构建一切。人们把1000视为权威,我们反其道行之,捍卫1024的地位。我们不是键盘侠,我们只是平凡世界中不凡的缔造者 。
相关文章
|
1月前
|
索引 Python
python-类属性操作
【10月更文挑战第11天】 python类属性操作列举
16 1
|
6月前
|
缓存 Java Python
python-静态方法staticmethod、类方法classmethod、属性方法property_python staticmethod类内使用(1)
python-静态方法staticmethod、类方法classmethod、属性方法property_python staticmethod类内使用(1)
|
2月前
|
存储 API 索引
让 Python 的属性查找具有 C 一级的性能
让 Python 的属性查找具有 C 一级的性能
16 0
|
2月前
|
Python
Python中类属性与实例属性的区别
了解这些区别对于编写高效、易维护的Python代码至关重要。正确地使用类属性和实例属性不仅能帮助我们更好地组织代码,还能提高代码运行的效率。
26 0
|
2月前
|
Python
Python类中属性和方法区分3-8
Python类中属性和方法区分3-8
|
3月前
|
程序员 开发者 Python
Python动态属性与反射机制方式
通过反射和动态属性,Python程序员获得了巨大的权能,能在运行时访问、修改或为对象新增属性和方法,显著提高编程的智能化和适应性。内置的反射机制可能使开发者跨越编写代码时的限制,通过名称访问对象的特性、方法以及其他成员,为创建一个具有高度配置性、扩展性强大的应用程序打下基础。此外,利用getattr和setattr函数来获取和设定对象的属性,或是利用hasattr确认其是否存在某属性,甚至可以通过名字来动态地执行对象的函数。 总之,反射和动态属性对于Python的程序开发而言是重要的工具,它们不仅提供了编写效率高且灵活的代码的能力,还为构建可高度定制和扩展的应用程序提供了可能。对于熟练掌握这些
|
4月前
|
API Python
python属性错误(AttributeError)
【7月更文挑战第13天】
199 10
|
4月前
|
程序员 开发者 Python
Python动态属性与反射机制方式
通过反射和动态属性,Python程序员获得了巨大的权能,能在运行时访问、修改或为对象新增属性和方法,显著提高编程的智能化和适应性。内置的反射机制可能使开发者跨越编写代码时的限制,通过名称访问对象的特性、方法以及其他成员,为创建一个具有高度配置性、扩展性强大的应用程序打下基础。此外,利用getattr和setattr函数来获取和设定对象的属性,或是利用hasattr确认其是否存在某属性,甚至可以通过名字来动态地执行对象的函数。 总之,反射和动态属性对于Python的程序开发而言是重要的工具,它们不仅提供了编写效率高且灵活的代码的能力,还为构建可高度定制和扩展的应用程序提供了可能。对于熟练掌握这些
|
5月前
|
存储 程序员 Python
Python类属性与实例属性详解
Python 中区分类属性和实例属性的设计是为了满足不同的需求和使用场景。这种区分使得代码更加灵活、清晰,并且能够提供更好的封装性和可维护性。类属性用于表示与整个类相关的数据,而实例属性则用于表示每个实例的特定信息。这样,我们可以将关注点分离开来,使得代码更易于理解、维护和扩展。在实际应用中,我们可以根据具体的情况,选择适当的属性类型来组织和管理代码。
50 1
|
5月前
|
存储 搜索推荐 Python
【随手记】python语法:类属性和实例属性
【随手记】python语法:类属性和实例属性
61 1