Python 常用魔法方法

1.dict

无处不在的__dict__

的__dict__属性和类对象的__dict__属性

# -*- coding: utf-8 -*-

class A(object):
    """
    Class A.
    """

    a = 0
    b = 1

    def __init__(self):
        self.a = 2
        self.b = 3

    def test(self):
        print('a normal func.')

    @staticmethod
    def static_test(self):
        print('a static func.')

    @classmethod
    def class_test(self):
        print('a calss func.')


obj = A()
print(A.__dict__)
print(obj.__dict__)

运行结果如下

{'__module__': '__main__', '__doc__': '\n    Class A.\n    ', 'a': 0, 'b': 1, '__init__': <function A.__init__ at 0x7f2fd92a0268>, 'test': <function A.test at 0x7f2fcabf3ea0>, 'static_test': <staticmethod object at 0x7f2fd9299ba8>, 'class_test': <classmethod object at 0x7f2fd9299c88>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>}
{'a': 2, 'b': 3}

由此可见, 类的静态函数、类函数、普通函数、全局变量以及一些内置的属性都是放在__dict__里的;

对象的__dict__中存储了一些self.xxx的一些东西.

Python里什么没有__dict__属性

虽然说一切皆对象,但对象也有不同,就好比不是每个人的女朋友都是一个人一样,一些内置的数据类型是没有__dict__属性的.

int, list, dict等这些常用的数据类型是没有__dict__属性的,其实这是可预料的,就算给了它们dict属性也没啥用,毕竟它们只是用来做数据容器的。

发生继承时候的__dict__属性

子类有自己的__dict__, 父类也有自己的__dict__,子类的全局变量和函数放在子类的dict中,父类的放在父类dict中。

总结

1) 内置的数据类型没有__dict__属性

2) 每个类有自己的__dict__属性,就算存着继承关系,父类的__dict__ 并不会影响子类的__dict__

3) 对象也有自己的__dict__属性, 存储self.xxx 信息,父子类对象公用__dict__

2. 构造方法

我们最为熟知的基本的魔法方法就是__init__ ,我们可以用它来指明一个对象初始化的行为。然而,当我们调用 x = SomeClass() 的时候__init__ 并不是第一个被调用的方法。事实上,第一个被调用的是__new__ ,这个 方法才真正地创建了实例。当这个对象的生命周期结束的时候,__del__会被调用。让我们近一步理解这三个方法:

  • __new__(cls,[...)
    __new__ 是对象实例化时第一个调用的方法,它只取下 cls 参数,并把其他参数传给 __init__ 。 __new__ 很少使用,但是也有它适合的场景,尤其是当类继承自一个像元组或者字符串这样不经常改变的类型的时候。我不打算深入讨论__new__ ,因为它并不是很有用, Python文档 中 有详细的说明。

  • __init__(self,[...])
    类的初始化方法。它获取任何传给构造器的参数(比如我们调用 x = SomeClass(10, ‘foo’) , __init__ 就会接到参数 10 和 ‘foo’ 。__init__在Python的类定义中用的最多。

  • __del__(self)
    __new__ 和 __init__ 是对象的构造器, __del__ 是对象的销毁器。它并非实现了语句 del x (因此该语句不等同于 x.__del__())。而是定义了当对象被垃圾回收时的行为。 当对象需要在销毁时做一些处理的时候这个方法很有用,比如 socket 对象、文件对象。但是需要注意的是,当Python解释器退出但对象仍然存活的时候, __del__ 并不会 执行。 所以养成一个手工清理的好习惯是很重要的,比如及时关闭连接。

3.操作符

比较操作符

Python包含了一系列的魔法方法,用于实现对象之间直接比较,而不需要采用方法调用。同样也可以重载Python默认的比较方法,改变它们的行为。下面是这些方法的列表:

  • __cmp__(self, other)
    __cmp__ 是所有比较魔法方法中最基础的一个,它实际上定义了所有比较操作符的行为(<,==,!=,等等),但是它可能不能按照你需要的方式工作(例如,判断一个实例和另一个实例是否相等采用一套标准,而与判断一个实例是否大于另一实例采用另一套)。 __cmp__ 应该在 self < other 时返回一个负整数,在self == other 时返回0,在 self > other 时返回正整数。最好只定义你所需要的比较形式,而不是一次定义全部。 如果你需要实现所有的比较形式,而且它们的判断标准类似,那么 __cmp__ 是一个很好的方法,可以减少代码重复,让代码更简洁。

  • __eq__`(self, other)
    定义等于操作符(==)的行为。

  • __ne__(self, other)
    定义不等于操作符(!=)的行为。

  • __lt__(self, other)
    定义小于操作符(<)的行为。

  • __gt__(self, other)
    定义大于操作符(>)的行为。

  • __le__(self, other)
    定义小于等于操作符(<)的行为。

  • __ge__(self, other)
    定义大于等于操作符(>)的行为。

算数操作符

数值操作符

这里把它们分成了五类:一元操作符,常见算数操作符,反射算数操作符(后面会涉及更多),增强赋值操作符,和类型转换操作符。以一元操作符和常见算数操作符举例.
一元操作符

一元操作符只有一个操作符。

  • __pos__(self)
    实现取正操作,例如 +some_object。

  • __neg__(self)
    实现取负操作,例如 -some_object。

  • __abs__(self)
    实现内建绝对值函数 abs() 操作。

  • __invert__(self)
    实现取反操作符 ~。

  • __round__(self, n)
    实现内建函数 round() ,n 是近似小数点的位数。

  • __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)
    实现 true 除法,这个函数只有使用 from __future__ import division 时才有作用。

  • __mod__(self, other)
    实现 % 取余操作。

  • __divmod__(self, other)
    实现 divmod 内建函数。

  • __pow__
    实现 ** 操作符。

  • __lshift__(self, other)
    实现左移位运算符 << 。

  • __rshift__(self, other)
    实现右移位运算符 >> 。

  • __and__(self, other)
    实现按位与运算符 & 。

  • __or__(self, other)
    实现按位或运算符 | 。

  • __xor__(self, other)
    实现按位异或运算符 ^ 。

4. 类的表示

  • __str__(self)
    定义对类的实例调用 str() 时的行为。

  • __repr__(self)
    定义对类的实例调用 repr() 时的行为。 str() 和 repr() 最主要的差别在于“目标用户”。 repr() 的作用是产生机器可读的输出(大部分情况下,其输出可以作为有效的Python代码),而 str() 则产生人类可读的输出。

  • __format__(self)
    定义当类的实例用于新式字符串格式化时的行为,例如,“Hello, 0:abc!”.format(a) 会导致调用 a.__format__(“abc”) 。当定义你自己的数值类型或字符串类型时,你可能想提供某些特殊的格式化选项,这种情况下这个魔法方法会非常有用。

  • __dir__(self)
    定义对类的实例调用 dir() 时的行为,这个方法应该向调用者返回一个属性列表。一般来说,没必要自己实现 __dir__ 。但是如果你重定义了 __getattr__或者 __getattribute__ (下个部分会介绍),乃至使用动态生成的属性,以实现类的交互式使用,那么这个魔法方法是必不可少的。

      到这里,我们基本上已经结束了魔法方法指南中无聊并且例子匮乏的部分。既然我们已经介绍了较为基础的魔法方法,是时候涉及更高级的内容了。
    

5. 访问控制

很多从其他语言转向Python的人都抱怨Python的类缺少真正意义上的封装(即没办法定义私有属性然后使用公有的getter和setter)。然而事实并非如此。实际上Python不是通过显式定义的字段和方法修改器,而是通过魔法方法实现了一系列的封装。

  • __getattr__(self, name)
    当用户试图访问一个根本不存在(或者暂时不存在)的属性时,你可以通过这个魔法方法来定义类的行为。这个可以用于捕捉错误的拼写并且给出指引,使用废弃属性时给出警告(如果你愿意,仍然可以计算并且返回该属性),以及灵活地处理AttributeError。只有当试图访问不存在的属性时它才会被调用,所以这不能算是一个真正的封装的办法。

  • __setattr__(self, name, value)
    和 __getattr__ 不同, __setattr__ 可以用于真正意义上的封装。它允许你自定义某个属性的赋值行为,不管这个属性存在与否,也就是说你可以对任意属性的任何变化都定义自己的规则。然后,一定要小心使用 __setattr__ ,这个列表最后的例子中会有所展示。

  • __delattr__(self, name)
    这个魔法方法和 setattr 几乎相同,只不过它是用于处理删除属性时的行为。和 setattr_ 一样,使用它时也需要多加小心,防止产生无限递归(在__delattr__ 的实现中调用 del self.name 会导致无限递归)。

  • __getattribute__(self, name)
    __getattribute__ 看起来和上面那些方法很合得来,但是最好不要使用它。__getattribute__ 只能用于新式类。在最新版的Python中所有的类都是新式类,在老版Python中你可以通过继承 object 来创建新式类。 __getattribute__ 允许你自定义属性被访问时的行为,它也同样可能遇到无限递归问题(通过调用基类的__getattribute__ 来避免)。__getattribute__ 基本上可以替代 __getattr__ 。只有当它被实现,并且显式地被调用,或者产生 AttributeError 时它才被使用。 这个魔法方法可以被使用(毕竟,选择权在你自己),我不推荐你使用它,因为它的使用范围相对有限(通常我们想要在赋值时进行特殊操作,而不是取值时),而且实现这个方法很容易出现Bug。

部分内容参考此处:Python魔法方法指南


在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值