美文网首页
30.4-魔术方法—operator模块_运算符重载

30.4-魔术方法—operator模块_运算符重载

作者: BeautifulSoulpy | 来源:发表于2019-12-24 15:55 被阅读0次

    每个明天都是从今天开始,我们能做的是迎接新的一天, 不念过往,不畏将来,做最好的自己。保持一颗年轻的心,做个简单的人,享受阳光和温暖。抛却一切杂念,于凡尘中静守一份安宁,悠然前行。早安,圣诞节!

    总结:

    1. 所有的魔术方法都跟实例self相关;
    2. 对象object 之间的比较是比较内存地址;

    functools, itertools, operator是Python标准库为我们提供的支持函数式编程的三大模块,合理的使用这三个模块,我们可以写出更加简洁可读的Pythonic代码;

    1. operator模块

    operator 模块提供了一套与 Python 的内置运算符对应的高效率函数。

    1.1 函数的种类

    函数包含的种类有:对象的比较运算、逻辑运算、数学运算和序列运算

    比较运算

    运算 函数 语法
    小于 lt(a, b) a < b
    小于等于 le(a, b) a <= b
    大于 gt(a, b) a > b
    大于等于 ge(a, b) a >= b
    等于 eq(a, b) a == b
    不等于 ne(a, b) a != b
    >>> from operator import *
    >>> lt(1, 2)     
    True
    >>> le(1, 3)    
    True
    >>> le(3, 3)   
    True
    >>> gt(10, 1)   
    True
    >>> ge(10, 10)
    True
    >>> ge(10, 9)  
    True
    >>> eq(10, 9) 
    False
    >>> ne(10,10) 
    False
    >>> ne(10, 9)
    True
    

    逻辑运算

    运算 函数 语法
    and_(a, b) a & b
    or_(a, b) a
    异或 xor(a, b) a ^ b
    取反 invert(a, b) ~ a
    对象是否相等 is_(a, b) a is b
    对象是否不相等 is_not(a, b) a is not b
    真值 truth(obj) obj
    >>> from operator import *
    >>> and_(1, 1)
    1
    >>> or_(1, 2)
    3
    >>> xor(1, 2)
    3
    >>> invert(True)
    -2
    >>> invert(1)
    -2
    >>> invert(2)
    -3
    >>> a = [1, 2, 3]
    >>> b = 3
    >>> is_(a, b)
    False
    >>> is_not(a, b)
    True
    >>> truth(a)
    True
    

    数学运算

    运算 函数 语法
    add(a ,b) a + b
    truediv(a, b) a / b
    mul(a, b) a * b
    sub(a, b) a - b
    pow(a, b) a ** b
    负数 neg(a) - a
    正数 pos(a) + a
    取模 mod(a, b) a % b
    >>> from operator import *
    >>> add(1, 2)
    3
    >>> truediv(3, 2)
    1.5
    >>> mul(3, 2)
    6
    >>> sub(3, 2)
    1
    >>> pow(2, 8)
    256
    >>> neg(5)
    -5
    >>> neg(-5)
    5
    >>> pos(10)
    10
    >>> pos(-10)
    -10
    >>> mod(10, 3)
    1
    

    序列运算

    运算 函数 语法
    字符串拼接 concat(seq1, seq2) seq1 + seq2
    包含 contains(seq, obj) obj in seq
    索引赋值 setitem(obj, i, v) obj[i] = v
    索引删除 delitem(obj, i) del obj[i]
    索引取值 getitem(obj, i) obj[i]
    切片赋值 setitem(seq, slice(i, j), values) seq[i:j] = values
    切片删除 delitem(seq, slice(i, j)) del seq[i:j]
    切片取值 getitem(seq, slice(i, j)) seq[i:j]
    格式化 mod(s, obj) s % obj
    >>> from operator import *
    >>> concat('hello', ' Python')
    'hello Python'
    >>> a = [1, 3, 4]
    >>> contains(a, 2)
    False
    >>> setitem(a, 1, 5)
    >>> a
    [1, 5, 4]
    >>> delitem(a, 2)
    >>> a
    [1, 5]
    >>> getitem(a, 1)
    5
    >>> setitem(a, slice(1, 3), 'ijk')
    >>> a
    [1, 'i', 'j', 'k']
    >>> delitem(a, slice(2, 3))
    >>> a
    [1, 'i', 'k']
    >>> mod('str %s', 'value')
    'str value'
    

    有了这些方法之后,我们可以快速实现两个类之间的相加相减相除;

    2. 运算符的重载

    运算符重载:很多运算符只能作用于内置类型,通过重载就可以赋予它们更多的含义,让它们可以作用于对象。
    重载运算符让运算符有新的语义,但绝对不是改变它的语法。

    class A:
        def __init__(self,name,age=18):
            self.name=name
            self.age=age
        def __sub__(self,other):  # 魔术方法都是建立在实例self的基础之上;
            return self.age - other.age
            # return self
            return self.__class__(self.name,self-other)  # self-other 两个实例的减法;
        def __isub__(self,other):  # 魔术方法都是建立在实例self的基础之上;
            # self.age -= other.age
            # return self
            return self.__class__(self.name,self-other)  # self-other 两个实例的减法;
            
    tom = A('tom')
    jerry = A('jerry',16)
    
    print(tom - jerry)
    print(tom.__sub__(jerry))  # 等价减去- ;
    # tom = tom -jerry
    
    print(id(tom))
    tom -= jerry
    # print(tom.__isub__(jerry))
    # tom = tom - jerry
    print(tom.name)
    #---------------------------------------------------------------------------------------
    2
    2
    2165910966512
    tom
    

    总结:
    _isub_ 方法定义,一般会in-place来修改自身;如果没有定义 _isub_ 方法,则会调用 _sub_

    练习:
    完成Point类设计,实现哦安短点相等的方法;并完向量的加法;

    1. 在直角坐标系里面,定义原点为向量的起点,两个向量和差的坐标分别等于这两个向量坐标的和与差若向量的表示为(x,y)形式;
    class Point:
        def __init__(self,x,y):
            self.x = x
            self.y = y
        def __add__(self,other):
            return Point(self.x+other.x,self.y+other.y) # 返回一个新的;原来的类型
    
        def add(self,other):
            return (self.x+other.x,self.y+other.y)
        
        def __iadd__(self,other): 
            self.x,self.y = self.x + other.x,self.y + other.y # 就地修改;
            return self
        
        def __str__(self):
            return '<Point: {},{}>'.format(self.x, self.y)
    p1 = Point(3,4)
    p2 = Point(1,2)
    
    p3 = p1 + p2  #
    p1 += p2
    print(p3,p1)
    #-----------------------------------------------------------------------------------------------
    <Point: 4,6> <Point: 4,6>
    

    加 @total_ordering 装饰器6种符号都可以使用; 有点鸡肋,效率不高;
    _eq_ 等于可以推断不等于
    _gt_ 大于可以推断小于
    _ge_ 大于等于可以推断小于等于
    也就是用3个方法,就可以把所有比较解决了,所以total_ordering可以不使用

    from functools import total_ordering
    
    @total_ordering
    class Point:
        def __init__(self,x,y):
            self.x = x
            self.y = y
        def __add__(self,other):
            return Point(self.x+other.x,self.y+other.y) # 返回一个新的;
        def add(self,other):
            return (self.x+other.x,self.y+other.y)
        def __iadd__(self,other): 
            self.x,self.y = self.x + other.x,self.y + other.y # 就地修改;
            return self
        
        def __eq__(self,other):
            return self.x == other.x  # 相等=:只看x轴;
        def __gt__(self,other):      
            return self.x > other.x   # 大于> :只看x轴;
    p1 = Point(3,4)
    p2 = Point(1,2)
    p3 = Point(3,5)
    
    print(p1 == p3)
    # 加装饰器total_ordering 6种符号都可以用;
    print(p1 == p2)
    print(p1 != p2)
    print('-'*50)
    print(p1 > p2)
    print(p1 >= p2)
    print(p1 < p2)
    print(p1 <= p2)
    #------------------------------------------------------
    False
    True
    True
    --------------------------------------------------
    True
    True
    False
    False
    
    
    等价形式:(实用形式:比装饰器效率高)
    class Point:
        def __init__(self,x,y):
            self.x = x
            self.y = y
            
        def __add__(self,other):
            return Point(self.x+other.x,self.y+other.y) # 返回一个新的;
        
        def add(self,other):
            return (self.x+other.x,self.y+other.y)
        
        def __iadd__(self,other): 
            self.x,self.y = self.x + other.x,self.y + other.y # 就地修改;
            return self
        
        def __eq__(self,other):
            return self.x == other.x  # 相等=:只看x轴;
        
        def __gt__(self,other):      
            return self.x > other.x   # 大于> :只看x轴;
        
        def __ge__(self,other):
            return self.x >= other.x  
    p1 = Point(3,4)
    p2 = Point(1,2)
    p3 = Point(3,5)
    
    print(p1 == p2)
    print(p1 != p2)
    print(p1 > p2)
    print(p1 >= p2)
    print(p1 < p2)
    print(p1 <= p2)
    #----------------------------------------------------------------------
    False
    True
    True
    True
    False
    False
    

    运算符重载应用场景
    往往是用面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式。例如,上例中
    的对+进行了运算符重载,实现了Point类的二元操作,重新定义为Point + Point。
    提供运算符重载,比直接提供加法方法要更加适合该领域内使用者的习惯。
    int类,几乎实现了所有操作符,可以作为参考。

    @functools.total_ordering 装饰器
    _lt_ , _le_ , _eq_ , _gt_ , _ge_ 是比较大小必须实现的方法,但是全部写完太麻烦,使用
    @functools.total_ordering 装饰器就可以大大简化代码。
    但是要求 _eq_ 必须实现,其它方法 _lt_ , _le_ , _gt_ , _ge_ 实现其一

    相关文章

      网友评论

          本文标题:30.4-魔术方法—operator模块_运算符重载

          本文链接:https://www.haomeiwen.com/subject/knxqoctx.html