python基础(十)python中魔法方法以及云算符的重载

(一)魔法方法即类中的特殊方法
  Python 的对象天生拥有一些神奇的方法,它们总被双下划线所包围,他们是面向对象的 Python 的一切。他们是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而这一切都是自动发生的。Python 的魔术方法非常强大,然而随之而来的则是责任。了解正确的方法去使用非常重要!
  · 下面介绍几个基本的魔法方法
假设一个类A中定义了以下特殊方法(魔法方法),创建类实例名为a。
1.__ new__(cls, …):a) __ new__ 是在一个对象实例化的时候所调用的第一个方法。 b)它的第一个参数是这个类,其他的参数是用来直接传递给 __ init__ 方法的。 c) __ new__ 决定是否要使用该 __ init__ 方法,因为 __ new__ 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __ new__ 没有返回实例对象,则 __ init__ 不会被调用。 d)__ new__ 主要是用于继承一个不可变的类型比如一个 tuple 或者 string。
2.__ init__(self, …)构造器,当一个实例被创建的时候调用的初始化方法
3.__ call__(self, args…) 允许一个类的实例像函数一样被调用:语法a(arg1, arg2,…) 等同于a.__ call__(arg1, arg2,…)
4.__ str__(self) a)定义当被 str() 调用时的行为 ,b)当print一个类对象时也将会自动调用它,比如定义了A类,如果定义了该魔法方法print(A())将得到该方法的返回值(必须是字符串类型),否则打印出来的将会类似是这样的<__ main__.A object at 0x00000003156CC940>。

class A(object):
    def __init__(self, x):
        self.x = x
        print('x in __init__', x)
    def __new__(cls, y):
        print('y in __new__', y)
        return super(A, cls).__new__(cls)
    def __call__(self, z):
        print('z in __call__', z)
        return 0
    def __str__(self):
        return '{}'.format(self.x)

a = A('123')
print(a)
call_res = a('abc')
print(call_res)
"""
结果:
y in __new__ 123
x in __init__ 123
123
z in __call__ abc
0
结果分析:
1.可以看到当执行A('123')时:调用顺序是先调用__new__创建对象然后调用__init__初始化对象
2.接下来执行print(a),如果没有定义__str__,你打印A对象将得到:<__main__.A object at 0x000000533C30A828>
3.接下来执行a('abc'),生成对象a后可以将对象像函数一样调用a('abc'),与这个函数绑定的函数体是__call__魔法方法。我们可以通过对象来直接调用类中的特殊方法__call__(),比如keras中我创建一个层实例然后通过实例名然后传入张量便可以调用call方法然后得到一个张量结果。我们在自定义层时也需要通过实现call方法来定义张量的逻辑运算。
"""

5.__ del__(self) 析构器,当一个实例被销毁的时候调用的方法
6.__ len__(self) 定义当被 len() 调用时的行为
7.__ repr__(self) 定义当被 repr() 调用时的行为
8.__ hash__(self) 定义当被 hash() 调用时的行为
9.__ format__(self, format_spec) 定义当被 format() 调用时的行为
10.__ divmod__(self, other) 定义当被 divmod() 调用时的行为
11.__ abs__(self) 定义当被 abs() 调用时的行为

运算符对应的魔法方法 说明
__ lt__(self, other) 定义小于号的行为:x < y 调用 x.__ lt__(y)
__ le__(self, other) 定义小于等于号的行为:x <= y 调用 x.__ le__(y)
__ eq__(self, other) 定义等于号的行为:x == y 调用 x.__ eq__(y)
__ ne__(self, other) 定义不等号的行为:x != y 调用 x.__ ne__(y)
__ gt__(self, other) 定义大于号的行为:x > y 调用 x.__ gt__(y)
__ ge__ (self, other) 定义大于等于号的行为:x >= y 调用 x.__ ge__(y)
__ add__(self, other) 定义加法的行为:+
__ sub__(self, other) 定义减法的行为:-
__ mul__(self, other) 定义乘法的行为:*
__ truediv__(self, other) 定义真除法的行为:/
__ floordiv__(self, other) 定义整数除法的行为://
__ mod__(self, other) 定义取模算法的行为:%
__ pow__(self, other[, modulo]) 定义当被 power() 调用或 ** 运算时的行为
__ lshift__(self, other) 定义按位左移位的行为:<<
__ rshift__(self, other) 定义按位右移位的行为:>>
__ and__(self, other) 定义按位与操作的行为:&
__ xor__(self, other) 定义按位异或操作的行为:^
__ or__(self, other) 定义按位或操作的行为:|
__ iadd__(self, other) 定义赋值加法的行为:+=
__ isub__(self, other) 定义赋值减法的行为:-=
__ imul__(self, other) 定义赋值乘法的行为:*=
__ pos__(self) 定义正号的行为:+x
__ neg__(self) 定义负号的行为:-x
__ invert__(self) 定义按位求反的行为:~x

(二)运算符重载
  从上面我说类的魔法方法时,大概就可以猜到,想实现运算符的重载只需要自己重写对应的魔法方法就行了。针对任意一种数据类型,我们都可以重写针对这种数据类型的运算符计算逻辑即运算符重载。需要注意的一点是重写了运算逻辑其返回值的数据类型应当是该类的数据类型,毕竟这种计算逻辑是只针对该类的。c++中是通过实现一个类方法A operator+(A )来实现A这种数据类型+运算符的重载。

class A(object):
    def __init__(self, x):
        self.x = x
        
    def __add__(self, other):# other是另一个A类型对象
        return A(self.x + other.x)
    
    def __str__(self):
        return '{}'.format(self.x) #只能返回字符串对象

r = A(1) + A(2)
print(r, type(r))
"""
结果:
3 
结果分析:
1.当A这种数据类型的对象后面跟了+运算,此时计算机是取调用__add__方法来实现+计算即A(1)+A(2)==A(1).__add__(A(2)),
返回的结果也是A类型的。实际上python内置的数据类型也是按这种方式来实现运算符的计算的。
2.因为实现了__str__,按其逻辑打印A的对象得到的是该对象的数据成员的值,而不是像这样:<__main__.A object at 0x000000533C30A828>
"""

  如果我不去重写__add__魔法方法它会不支持A这种数据类型的+运算操作。看:

class A(object):
    def __init__(self, x):
        self.x = x
        
    # def __add__(self, other):# other是另一个A类型对象
    #     return A(self.x + other.x)
    
    def __str__(self):
        return '{}'.format(self.x) #只能返回字符串对象

r = A(1) + A(2)
print(r, type(r))
"""
TypeError: unsupported operand type(s) for +: 'A' and 'A'
"""

参考:

你可能感兴趣的:(编程相关)