前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python中的魔法属性

Python中的魔法属性

作者头像
小小詹同学
发布2021-04-19 11:34:12
4720
发布2021-04-19 11:34:12
举报
文章被收录于专栏:小詹同学小詹同学

魔法属性

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

常见的魔法属性

__doc__

表示类的描述信息

代码语言:javascript
复制

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

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

__module__ 和 __class__

  • __module__ 表示当前操作的对象在那个模块
  • __class__ 表示当前操作的对象的类是什么
代码语言:javascript
复制

# __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__() 创建完对象后调用,对当前对象的一些实例初始化,无返回值
代码语言:javascript
复制

# __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__ 的调用是由解释器在进行垃圾回收时自动触发执行的。

代码语言:javascript
复制
# __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__ 在那些 类的实例经常改变状态的时候会非常有效。调用这个实例是一种改变这个对象状态的直接和优雅的做法。用一个实例来表达最好不过了:

代码语言:javascript
复制
# __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__

类或对象中的所有属性

类的实例属性属于对象;类中的类属性和方法等属于类,即:

代码语言:javascript
复制
# __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__方法,那么在打印 对象 时,默认输出该方法的返回值。

代码语言:javascript
复制
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__

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

字典示例
代码语言:javascript
复制
# __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

列表示例
代码语言:javascript
复制
# 切片操作
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 开始引进的关键词。你应该遇过这样子的代码:

代码语言:javascript
复制
with open('foo.txt') as bar:
    # do something with bar
    pass

with 声明的代码段中,我们可以做一些对象的开始操作和退出操作,还能对异常进行处理。这需要实现两个魔术方法: __enter__ 和 __exit__。

代码语言:javascript
复制
__enter__(self):

定义了当使用 with 语句的时候,会话管理器在块被初始创建时要产生的行为。请注意,__enter__ 的返回值与 with 语句的目标或者 as 后的名字绑定。

代码语言:javascript
复制
__exit__(self, exception_type, exception_value, traceback):

定义了当一个代码块被执行或者终止后,会话管理器应该做什么。它可以被用来处理异常、执行清理工作或做一些代码块执行完毕之后的日常工作。如果代码块执行成功,exception_type,exception_value,和traceback 将会为 None 。否则,你可以选择处理这个异常或者是直接交给用户处理。如果你想处理这个异常的话,请确保__exit__ 在所有语句结束之后返回 True。如果你想让异常被会话管理器处理的话,那么就让其产生该异常。

__copy__、__deepcopy__

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

代码语言:javascript
复制
__copy__(self):

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

代码语言:javascript
复制
__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插件

代码语言:javascript
复制
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 小詹学Python 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 魔法属性
  • 常见的魔法属性
    • __doc__
      • __module__ 和 __class__
        • __init__ 、__new__
          • __del__
            • __call__
              • __dict__
                • __str__
                  • __getitem__、__setitem__、__delitem__
                    • 字典示例
                    • 列表示例
                  • __enter__、__exit__
                    • __copy__、__deepcopy__
                    • 其他魔法方法
                      • 用于比较的魔术方法
                        • 数值计算的魔术方法
                          • 单目运算符和函数
                          • 双目运算符或函数
                          • 增量运算
                        • 类型转换
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档