Python笔记_第四篇_高阶编程_魔法(术)方法详解(重载的再详解)

1. 魔法方法是什么?

  魔法方法(Magic Method)是Python比较独特的应用,它可以给你的类增加特殊的方法,如果你的对象实现了(重载),这些方法中的某一个,就会被Python所调用。正如装饰器类似,是对类/函数部分的提炼(装饰器是对关键功能部分的封装)。一般情况下,是对类中方法和属性一种提炼。

2. 魔法方法详细解释

  前面我们用过__init__,__repr__,__str__等魔法方法,我们在这里做一个总结,查遗补漏,由于这个位置在Python中比较松散的表示。这里做一个分别的描述。

  2.1 基础魔法方法

  1. __new__(cls[, ...]) :初始化类构造函数

  2.__init__(self[, ...]):初始化/构造函数

  解释1:实例化对象时,第一个被调用的方法,其阐述直接传递给__init__方法处理,一般不会重写该方法。必须有一个参数clc,代表着要实例化的类,而且必须要有返回值,返回实例化出来的实例对象。好比制造产品之前的原材料采购环节。__init__好比原材料上进行加工的环节。

  解释2:创建一个构造函数。

  其中:1.来进行创建对象,这个new方法可以是object的,也可以是自己重写的,最后必须要return这个创建好的对象的引用。2.是进行实例对象的初始化,接收的就是new方法跑出来对象。

  实例1:__init__(self)

  这个方法就是一个构造函数,所谓构造函数的作用:1.通过类来进行传参,2.初始化类中的属性。

class Foo(object):


    def __init__(self,name):
        self.name = name

f = Foo("Thomas")

  实例2:type(第一个参数:类名,第二个参数:当前类的基类,第三个参数:类的成员)    

  上述代码中,obj是通过Foo类实例化的对象,其实,步进obj是一个对象,Foo类本身也是一个对象,因为在Python中一切皆是对象。如果按照一切事物都是对象的理论,obj对象是通过Foo类的构造方法创建的,那么Foo类对象应该也是通过执行某个类的构造方法创建的。

print(type(f)) # 表示,obj对象由Foo类创建  <class '__main__.Foo'>
print(type(Foo)) # 表示,Foo类对象由type类创建  <class 'type'>

  我们通过打印类型的值,obj对象由Foo类创建。Foo类对象由type类创建。所以,f对象是Foo类的一个实例,Foo类对象是type类的一个实例。即,Foo类对象是通过type类的构造方法创建的。

  那么,创建类就可以有两种方式:

  1.普通方式:

class Foo(object):
    def func(self):
        print("hello World")

  2. 特殊方式:

def func(self):
        print("hello World")

Foo = type('Foo',(object,),{'func':func})

print(type(Foo))
# <class 'type'>

f = Foo()
f.func()
# hello World

  其中:'func':func表示的是这个类中的方法,'Foo',(object,)表示为类名和继承基类。通过type这个函数我们用特殊方式创建了一个类。

  另外,我们还可以通过type的方式,把构造函数也纳入进来。

def func(self):
        print("hello World %s" % self.name)

def __init__(self,name):
    self.name = name

Foo = type('Foo',(object,),{'func':func,'__init__':__init__})

f = Foo("Thomas")
f.func()
# hello World Thomas

  实例3:__new__:创建一个类的实例化

class Foo(object):
    # __metaclass__ = MyType  # Foo通过__metaclass__来关联的

    def __init__(self,name):
        self.name = name

    def __new__(cls, *args, **kwargs):
        print("Foo---new__")
        return object.__new__(cls)  # 去继承父亲__new__方法

f = Foo("Thomas")
print(f.name)
# Foo---new__
# Thomas

   从这个例子当中我们可以发现这样一个规律:

  类——Foo这个对象——__init__这个对象,这样一个过程,因为一切皆对象,类也是个对象,__new__这个的作用就是把Foo这个对象装入object的这个类当中,其实在平时工作的时候很少写这个new函数。可以这么说,def __new__是吧Foo这个类放入进去了,然后,__init__这个有放入Foo这个类当中了。__new__起到的作用就是父类之前类的最本初状态

  我们在举几个相关的实例来理解这个__new__

  例子1:我们来使用类属性来表示当前是否第一次创建

class Dog(object):
    __instance = True # True表示是第一次创建
    __firstObj = None
    def __new__(cls):
        if cls.__instance:
            cls.__instance = False
            cls.__firstObj = object.__new__(cls)
            return cls.__firstObj # 如果是第一次创建 返回这个创建好的实例对象引用
        else:
            # 不是第一次创建  那么返回第一次创建好的对象引用
            return cls.__firstObj   

a = Dog()
b = Dog()
print( id(a) == id(b) )  # True

   例子2:这样就实现了功能,但是我们注意到里面有两个类的属性,简化一下,其实可以写成一个直接让_instance为None的值,第一次创建好对象之后将引用赋值给__instance即可。

class Dog(object):
    __instance = None
    def __new__(cls):
        if cls.__instance == None: # None 就表示还没创建过对象
            cls.__instance = object.__new__(cls)
            return cls.__instance # 如果是第一次创建 返回这个创建好的实例对象引用
        else:
            # 不是第一次创建  那么返回第一次创建好的对象引用
            return cls.__instance   

a = Dog()
b = Dog()
print( id(a) == id(b) ) 

  例子3:我们发现,上面进行了多次的初始化对象,虽然只会创建一次失利对象,但是__init__()会被重复调用来初始化失利对象。如果我们现在如果只想让实例对象初始化一次,可以这么写。

class Dog(object):
    __instance = None
    __hasInit = False # 思路也是一样的  新增一个类属性表示是否第一次初始化
    def __new__(cls):
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)
            return cls.__instance
        else:
            return cls.__instance
    
    def __init__(self):
        if self.__hasInit == False:
            print('----init------')
            self.__hasInit = True

a = Dog() # ----init------
b = Dog() # 不在重复初始化

  实例4:__metaclass__下面代码在Py3中不能执行,在Py2中可以看到这个详细的过程

  类默认是由type类实例化产生,type类中如何实现的创建类?类有事如何创建对象?类中有一个属性__metaclass__,其用来表示该类由谁来实例化创建,所以我们可以为__metclass__设置一个type类的拍成类,从而查看类的创建过程。

class MyType(type):
    def __init__(self,what,bases=None,dict=None):
        print("--MyType init---")
        super(MyType,self).__init__(what,bases,dict)

    def __call__(self, *args, **kwargs):
        print("--Mytype call--")
        obj = self.__new__(self,*args,**kwargs)

        self.__init__(obj,*args,**kwargs)


class Foo(object):
    __metaclass__ = MyType  # Foo通过__metaclass__来关联的

    def __init__(self,name):
        self.name = name

    def __new__(cls, *args, **kwargs):
        print("Foo---new__")
        return object.__new__(cls)

f = Foo("Thomas")
print(f.name)

# 先执行了:--MyType init---
# 后执行了:--Mytype call--
# 再执行了:Foo---new__"
# 最后执行了:Thomas

  

  小结:其实我们发现,我们在平时创建类的过程中,其实都是在用一个现存的模板来创建这个类(一般来说就是从__init__构造函数开始的)。其实在这个过程之前还有很多创建这个模板的过程。由此往前类推我的得到如下这样一个过程:

  Py元类解释器——type元类——metaclass元类属性——__new__——模板——__init__其他等等... ...这样一个过程。

  type----object---new---模板

  我们前面用了type的方式去创建一个类的方式而不是用线程模板的这种方式去创建一个类为的就是要解释这样一个过程。

  其中:在type元类(基类)中,我们通过__call__方法重新设计一个类,然后交给__new__看是否需要进一步修饰。其实在平时的工作中这个方法比较少用。但是底层的研究Py必须要知道。

  3.__call__(self[, args...]) 允许一个类的实例像函数一样被调用:x(a, b) 调用 x.__call__(a, b),不需要返回值。

  解释:如果一个类中定义了__call__方法,那么这个类它的实例就可以作为函数调用,也就实现了()运算符,即可以调用对象协议:

  实例1:

class TmpTest:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __call__(self, x, y):
        self.x, self.y = x, y


a = TmpTest(1, 2)
a(4, 5)
print(a.x, a.y)
# 5

  实例2:借用装饰器来讲解一个__call__方法的使用,如果需要将一个类作为装饰器,那么需要为这个类实现这个方法:

class TmpTest:
    def __init__(self, func):
        self.func=func

    def __call__(self, *args,**kwargs):
        result=self.func(*args,**kwargs)
        return result


@TmpTest
def add_num(x,y):
    return x+y

print(add_num(1,0))
# 1

  4.__del__(self) 析构方法,当实例化对象被彻底销毁时被调用(实例化对象的所有指针都被销毁时被调用)

  解释:很简单,相对于__init__构造函数这里就是一个西沟函数,用于在程序结束的时候释放内存空间,但是平时很少用,因为Py有自己的内存管理系统,会自动释放

  实例1:

class Person(object):
    def run(self):
        print("run")
    def eat(self, food):
        print("eat" + food)
    def __init__(self,name,age,height,weight):
        self.name=name
        self.age=age
        self.height=height
        self.weight=weight
    def __del__(self):#当程序结束时运行
        print("析构函数")
per1=Person("lili",20,175,50)
del per1  #手动释放对象
print(per1.name)#释放对象后程序不运行
#在函数里定义的对象,会在函数结束时自动释放,这样可以减少内存空间的浪费
def func():
    per2=Person("x",2,45,7)
func()

  

  5.__repr__(self) 定义当被 repr() 调用时的行为

  6. __str__(self) 定义当被 str() 调用时的行为

  这两者都是用来实现类的订制:
  方法5,在使用内建函数repr()进行自动调用,一般在黑屏终端中使用。

  方法6,在使用print自动调用。

  实例1:__str__

class Item(object):
    def __init__(self,name):
        self._name = name

    def __str__(self):
        return "Item's name is :" + self._name


I = Item("Thomas")

print(I)
# Item's name is :Thomas

  实例2:__repr__

class Item(object):
    def __init__(self,name):
        self._name = name

    def __repr__(self):
        return "Item's name is :" + self._name


I = Item("Thomas")

print(I)
print(type(I))
# <class '__main__.Item'>
# Item's name is :Thomas

  

  7. __len__(self) 定义当被 len() 调用时的行为

  我们在编写类中的对象的时候想要用类名或者实例化的名字返回对象的长度,需要在类中添加这个方法,直接用len会报错。

  实例1:

class Students(object):

    def __init__(self, *args):
        self.names = args

    def __len__(self):
        return len(self.names)

s = Students('tom', 'jack')
print( len(s) )
# 2

  

  8. __bytes__(self) 定义当被 bytes() 调用时的行为

  只有很少的情景我们要把对象转换成为字节。在一些特殊的情况下,在吸入文件之前,我们需要将一个对象直接编码成字节。通常使用字符串并且使用str类型为我们体用字符串的字节表示惠更简单。注意,有一些__init__()方法会将一个数值类型的rank转换一个字符串,导致丢失原始的数值。为了使字节编码可逆,我们需要重新创建rank的原始数值。

  实例1:通过__bytes__来实现,返回Card、rank和suit的UTF-8编码。

class Test(object):

    def __bytes__(self):
        class_code = self.__class__.__name__[0]
        rank_number_str = {'A': '1', 'J': '11', 'Q': '12', 'K': '13'}.get(self.rank, self.rank)
        string = "(" + " ".join([class_code, rank_number_str, self.suit, ]) + ")"
        return bytes(string, encoding="utf8")

  这种实现首先用字符串表示Card对象,然后将字符串编码为字节。通常是最简单最灵活的方法。

  实例2:当我们拿到一串字节时,我们可以将这串字节解码为一个字符串,然后将字符串转换为一个新的Card对象。下面是基于字节创建Card对象的方法。

def card_from_bytes( buffer ):
   string = buffer.decode("utf8")
   assert string[0 ]=="(" and string[-1] == ")"
   code, rank_number, suit = string[1:-1].split()
   class_ = { 'A': AceCard, 'N': NumberCard, 'F': FaceCard }[code]
   return class_( int(rank_number), suit )
# 我们可以像下面这样生成一个Card对象的字节表示。

b= bytes(someCard)

# 我们可以用生成的字节重新创建Card对象。

someCard = card_from_bytes(b)

  

  9. __hash__(self) 定义当被 hash() 调用时的行为

  解释:与__eq__类似,但是!如果这个类无法作为哈希集合的元素使用,比如hashable collections指的是:set,frozenset和dict就不能用__eq__了。也属于中比较运算符。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __hash__(self):
        print(self.name, '使用了__hash__方法')
        return hash(self.name)

    # def __eq__(self, other):
    #     print(self.name, '使用了__eq__方法')
    #     return self.__dict__ == other.__dict__


person1 = Person('zs', 20)
person2 = Person('ls', 20)
person3 = Person('ww', 30)
person4 = Person('zs', 20)

set1 = {person1, person2, person3, person4}
# zs 使用了__hash__方法
# ls 使用了__hash__方法
# ww 使用了__hash__方法
# zs 使用了__hash__方法

  

  10. __bool__(self) 定义当被 bool() 调用时的行为,应该返回 True 或 False

class Test1(object):
    pass

class Test2(object):
    def __bool__(self):
        return False

    def __len__(self):
        return 0

class Test3(object):
    def __bool__(self):
        return False

    # def __len__(self):
    #     return 0


test1 = Test1()
test2 = Test2()
test3 = Test3()
print(bool(test1))  # 没有定义bool和len对象,默认为True
print(bool(test2))  # bool函数决定布尔值,返回False
print(bool(test3))  # 有bool函数,则由len函数决定bool值,返回False

#True
#False
#False

  

  11. __format__(self, format_spec) 定义当被 format() 调用时的行为

  实例1:

class formatest(object):
    def __init__(self, name, age):
        self.name,self.age = name, age

    def __format__(self,specification):
        if specification == "":
            return str(self)
        strformat = specification.replace("%s",self.name).replace("%r",self.age)
        return strformat

if __name__ == "__main__":
    people = formatest("Thomas", "89")
    print ("{}".format(people))
    print ("{0:%s-%r}".format(people))
    print (format(people, "%s-%r"))

# <__main__.formatest object at 0x000001CDB4848F60>
# Thomas-89
# Thomas-89

  

  2.2 属性相关方法

  12. __getattr__(self, name) 定义当用户试图获取一个不存在的属性时的行为

  13. __getattribute__(self, name) 定义当该类的属性被访问时的行为

  实例1:__getattr__,当属性查找不到时会报错,如果重载__getattr在查找不到属性的时候就不会报错了。

from datetime import date

class User(object):
    def __init__(self,name,birthday):
        self.name = name
        self.birthday = birthday

if __name__ == '__main__':
    user = User("hobby",date(year=1987,month=1,day=1))
    print(user.age)
# Traceback (most recent call last):
#   File "F:/QUANT/练习/temp.py", line 10, in <module>
#     print(user.age)
# AttributeError: 'User' object has no attribute 'age'
from datetime import date

class User(object):
    def __init__(self,name,birthday):
        self.name = name
        self.birthday = birthday

    def __getattr__(self, item):
        return "not find attr"

if __name__ == '__main__':
    user = User("hobby",date(year=1987,month=1,day=1))
    print(user.age)
# not find attr

  实例2:如果我们写错了属性名字,还可以通过__getattr__修正过来。

from datetime import date

class User(object):
    def __init__(self,name,birthday):
        self.name = name
        self.birthday = birthday

    def __getattr__(self, item):
        return self.name

if __name__ == '__main__':
    user = User("hobby",date(year=1987,month=1,day=1))
    print(user.Name)
# hobby

  实例3:我们调用了user.company,但是在company在info里面,不能直接访问,这样我们可以在__getattr__写逻辑让user.company直接访问。

from datetime import date

class User(object):
    def __init__(self,name,birthday,info):
        self.name = name
        self.birthday = birthday
        self.info = info

    def __getattr__(self, item):
        return self.info[item]

if __name__ == '__main__':
    user = User("hobby",date(year=1987,month=1,day=1),info={"company":"CCTV"})
    print(user.company)
# CCTV

  实例4:__getattribute__。当每次调用属性时,Py都会无条件的进入这个里面,不论属性存在与否,这就是和__getattr__的区别。

from datetime import date

class User(object):
    def __init__(self,name,birthday,info):
        self.name = name
        self.birthday = birthday
        self.info = info

    def __getattribute__(self, item):
        return "hobby1"

if __name__ == '__main__':
    user = User("hobby",date(year=1987,month=1,day=1),info={"company":"CCTV"})
    print(user.name)
    print(user.age)  # 调用存在的属性
    
# hobby1
# hobby1

  实例5:必须特别小心getattribute方法,因为Py在查找类的方法名称时对其进行调用。

from datetime import date

class User(object):
    def __init__(self,name,birthday,info):
        self.name = name
        self.birthday = birthday
        self.info = info

    def __getattribute__(self, item):
        raise ArithmeticError
    
    def get_name(self):
        print(self.name)

if __name__ == '__main__':
    user = User("hobby",date(year=1987,month=1,day=1),info={"company":"CCTV"})
    user.get_name()

# 会产生报错。
# Traceback (most recent call last):
#   File "F:/QUANT/练习/temp.py", line 16, in <module>
#     user.get_name()
#   File "F:/QUANT/练习/temp.py", line 10, in __getattribute__
#     raise ArithmeticError
# ArithmeticError

  14. __get__(self, instance, owner) 定义当描述符的值被取得时的行为

  15. __set__(self, instance, value) 定义当描述符的值被改变时的行为

  16. __delete__(self, instance) 定义当描述符的值被删除时的行为

  解释:get表示调用一个苏醒是触发,set表示为一个属性赋值时触发,del表示删除属性时触发。

  概念:描述符:描述符就是一个新式类,在这新式类中,至少实现了这三种方法。描述符的作用是用来代理另外一个类的属性的(必须把面舒服定义成这个类的类属性,不能定义到构造函数中)

  实例1:

class Celsius:
    def __get__(self, instance, owner):
        return 5 * (instance.fahrenheit - 32) / 9

    def __set__(self, instance, value):
        instance.fahrenheit = 32 + 9 * value / 5

class Temperature:
    celsius = Celsius()
    def __init__(self, initial_f):
        self.fahrenheit = initial_f

t = Temperature(212)
print(t.celsius)
t.celsius = 0
print(t.fahrenheit)
# 100.0
# 32.0

  实例2:

class RevealAccess(object):
            """A data descriptor that sets and returns values
               normally and prints a message logging their access.
            """

            def __init__(self, initval=None, name='var'):
                self.val = initval
                self.name = name

            def __get__(self, obj, objtype):
                print 'Retrieving', self.name
                return self.val

            def __set__(self, obj, val):
                print 'Updating', self.name
                self.val = val

        >>> class MyClass(object):
        ...     x = RevealAccess(10, 'var "x"')
        ...     y = 5
        ...
        >>> m = MyClass()
        >>> m.x
        Retrieving var "x"
        >>> m.x = 20
        Updating var "x"
        >>> m.x
        Retrieving var "x"
        >>> m.y

  17.  __setattr__(self, name, value) 定义当一个属性被设置时的行为

  18. __delattr__(self, name) 定义当一个属性被删除时的行为

  解释1:__setattr__调用的类方法。会拦截所有属性的复制语句。如果定义了这个方法。self.arrt = value,就会变成self,__setattr__("attr",value),这个是需要注意的。挡在__setattr__方法内对属性进行赋值时,不可使用self.attr = value,因为他会再次被调用,会造成无穷递归循环,最后导致堆栈溢出异常。
  实例1:

class Dict(dict):
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"'Dict' object has no attribute '%s'" % key)

    def __setattr__(self, key, value):
        self[key] = value

d = Dict(a=1,b=2)
print (d['a'])
print (d.a) #调用类中原本没有定义的属性时候,调用__getattr__
d.a = 100 #对实例的属性进行赋值的时候调用__setattr__
print (d['a'])

# 1
# 1
# 100

  说明:继承来自dict的子类,所以self[attr] = value 相当于调用了dict的下标方法与a={};a{attr}=value 意思是一样的。

  实例2:

class MyClass(object):

    def __init__(self, work, score):
        self.work = work
        self.score = score

    def __delattr__(self, name):
        print("你正在删除一个属性")
        return super().__delattr__(name)


def main():
    test = MyClass(work="math", score=100)

    # 删除work属性
    del test.work

    # work属性删除,score属性还在
    print(test.score)

    try:
        print(test.work)
    except AttributeError as reason:
        print(reason)


if __name__ == '__main__':
    main()

  

  19. __dir__(self) 定义当 dir() 被调用时的行为

  解释:实际中__dict__,我们一般用的是self.__dict__的方法来查看对象的所有属性(因为他是以字典的方式返回的),而dir大多数使用dir(类名)的方式来查看类本身的所有方法。

  实例1:

class Student():

    def __init__(self, score, name, age):
        self.score = score
        self.name = name
        self.age = age


student = Student(80, "lihua", 18)
print(dir(student))
print("======")
print(dir(Student))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'score']

# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

  # 可以发现用实例化后对象比dir类本身多出了三个属性(age,name,score)

  实例2:

class Student():

    def __init__(self, score, name, age):
        self.score = score
        self.name = name
        self.age = age


student = Student(80, "lihua", 18)
print(student.__dict__)
print("======")
print(Student.__dict__)
# {'score': 80, 'name': 'lihua', 'age': 18}
# ======
# {'__module__': '__main__', '__init__': <function Student.__init__ at 0x000001BED6D35378>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

  实例3:

class Student():

    def __init__(self, score, name, age):
        self.score = score
        self.name = name
        self.age = age


student = Student(80, "lihua", 18)
print(student.__dir__())
print("======")
print(Student.__dir__())
# ['score', 'name', 'age', '__module__', '__init__', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
# ======
# Traceback (most recent call last):
#   File "F:/QUANT/练习/temp.py", line 12, in <module>
#     print(Student.__dir__())
# TypeError: descriptor '__dir__' of 'object' object needs an argument

  我们可以看到显示的效果和dir第一种的显示效果是一样的,如果直接对类取()的方法,会直接报错,如果不直接应用()汇显示类的特性而已。

  2.3 比较操作符:也叫比较运算符重载

  20. __lt__(self, other) 定义小于号的行为:x < y 调用 x.__lt__(y)

  21. __le__(self, other) 定义小于等于号的行为:x <= y 调用 x.__le__(y)

  22. __eq__(self, other) 定义等于号的行为:x == y 调用 x.__eq__(y)

  23. __ne__(self, other) 定义不等号的行为:x != y 调用 x.__ne__(y)

  24. __gt__(self, other) 定义大于号的行为:x > y 调用 x.__gt__(y)

     25. __ge__(self, other) 定义大于等于号的行为:x >= y 调用 x.__ge__(y)

    解释1:这些内容都差不多,解释一个即可。

  实例1:

class SavingsAccount(object):
    def __init__(self, name, pin, balance=0.0):
        self._name = name
        self._pin = pin
        self._balance = balance

    def __lt__(self, other):
        print("this is <")
        return self._name < other._name


s1 = SavingsAccount("Ken", "1000", 0)
s2 = SavingsAccount("Bill", "1001", 30)

print(s1<s2)
# this is <
# False

  

  2.4 算术运算符:也叫算术运算符重载

  26. __add__(self, other) 定义加法的行为:+
  27. __sub__(self, other) 定义减法的行为:-
  28.__mul__(self, other) 定义乘法的行为:*
  29. __truediv__(self, other) 定义真除法的行为:/
  30. __floordiv__(self, other) 定义整数除法的行为://
  31. __mod__(self, other) 定义取模算法的行为:%
  32. __divmod__(self, other) 定义当被 divmod() 调用时的行为
  33. __pow__(self, other[, modulo]) 定义当被 power() 调用或 ** 运算时的行为
  34. __lshift__(self, other) 定义按位左移位的行为:<<
  35. __rshift__(self, other) 定义按位右移位的行为:>>
  36. __and__(self, other) 定义按位与操作的行为:&
  37.__xor__(self, other) 定义按位异或操作的行为:^
  38. __or__(self, other) 定义按位或操作的行为:|

  解释1:这些内容也是差不多的,只解释一个就可以了。

  实例1:

class MyClass:

    def __init__(self, height, weight):
        self.height = height
        self.weight = weight

    # 两个对象的长相加,宽不变.返回一个新的类
    def __add__(self, others):
        return MyClass(self.height + others.height, self.weight + others.weight)

    # 两个对象的宽相减,长不变.返回一个新的类
    def __sub__(self, others):
        return MyClass(self.height - others.height, self.weight - others.weight)

    # 说一下自己的参数
    def intro(self):
        print("高为", self.height, " 重为", self.weight)


def main():
    a = MyClass(height=10, weight=5)
    a.intro()

    b = MyClass(height=20, weight=10)
    b.intro()

    c = b - a
    c.intro()

    d = a + b
    d.intro()


if __name__ == '__main__':
    main()
    
# 高为 10  重为 5
# 高为 20  重为 10
# 高为 10  重为 5
# 高为 30  重为 15

  2.5 反位运算符类似于运算方法

  39. __radd__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  40. __rsub__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  41. __rmul__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  42.__rtruediv__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  43. __rfloordiv__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  44. __rmod__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  45. __rdivmod__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  46. __rpow__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  47. __rlshift__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  48. __rrshift__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  49. __rxor__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用
  50. __ror__(self, other)当被运算对象(左边的操作对象)不支持该运算时被调用

  解释1:与上面一样,就是一个相反的运算,实例不举了。

  2.6 增量赋值运算符

  51. __iadd__(self, other) 定义赋值加法的行为:+=
  52. __isub__(self, other) 定义赋值减法的行为:-=
  53. __imul__(self, other) 定义赋值乘法的行为:*=
  54. __itruediv__(self, other) 定义赋值真除法的行为:/=
  55. __ifloordiv__(self, other) 定义赋值整数除法的行为://=
  56. __imod__(self, other) 定义赋值取模算法的行为:%=
  57. __ipow__(self, other[, modulo])定义赋值幂运算的行为:**=
  58. __ilshift__(self, other) 定义赋值按位左移位的行为:<<=
  59. __irshift__(self, other) 定义赋值按位右移位的行为:>>=
  60. __iand__(self, other) 定义赋值按位与操作的行为:&=
  61. __ixor__(self, other) 定义赋值按位异或操作的行为:^=
  62. __ior__(self, other) 定义赋值按位或操作的行为:|=

  解释:与前面的赋值运算符一样,就是变成增值的,实例也不举了。

  2.7 一元操作符

  63. __neg__(self) 定义正号的行为:+x
  64. __pos__(self) 定义负号的行为:-x
  65. __abs__(self) 定义当被 abs() 调用时的行为
  66. __invert__(self) 定义按位求反的行为:~x

  2.8 类型转换

  67. __complex__(self) 定义当被 complex() 调用时的行为(需要返回恰当的值)
  68. __int__(self) 定义当被 int() 调用时的行为(需要返回恰当的值)
  69. __float__(self) 定义当被 float() 调用时的行为(需要返回恰当的值)
  70. __round__(self[, n]) 定义当被 round() 调用时的行为(需要返回恰当的值)
  71. __index__(self)   1. 当对象是被应用在切片表达式中时,实现整形强制转换
            2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 __index__
            3. 如果 __index__ 被定义,则 __int__ 也需要被定义,且返回相同的值

  2.9 上下文管理(with语句)

  72. __enter__(self) 1. 定义当使用 with 语句时的初始化行为
            2. __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定
  73. __exit__(self, exc_type, exc_value, traceback) 1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么
                               2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作

  2.10 容器类型一般用于操作容器类

  74. __len__(self) 定义当被 len() 调用时的行为(一般返回容器类的长度)
  75. __getitem__(self, key) 定义获取容器中指定元素的行为,相当于 self[key]
  76. __setitem__(self, key, value) 定义设置容器中指定元素的行为,相当于 self[key] = value
  77. __delitem__(self, key) 定义删除容器中指定元素的行为,相当于 del self[key]
  78. __iter__(self) 定义当迭代容器中的元素的行为
  79. __reversed__(self) 定义当被 reversed() 调用时的行为
  80. __contains__(self, item) 定义当使用成员测试运算符(in 或 not in)时的行为

原文地址:https://www.cnblogs.com/noah0532/p/10932758.html