双下划线开头和双下划线按结尾的函数称为魔法函数,魔法函数一般都是在类里面使用,在触发的时候可以写其他的逻辑

# __call__:可以让对象带有函数的行为,通过()就可以调用
# 函数为什么可以使用()调用,是因为函数对象里面有__call__方法,而我们写的函数逻辑实际上是存在__call__这个方法里面的
def func():
    print("xxx")
    
print(dir(func)) # 可以看到这个函数的对象是有__call__方法的,这也是函数为什么可以使用()调用的原因
"""
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

"""

func()
"""
xxx
"""
func.__call__() # 使用这两个方法的结果是一样的,说明一个普通函数里面的逻辑是写在__call__下面的。
"""
xxx
"""

# 一般__call__魔法方法是和类一起使用,作用就是让类实例化的时候可以被调用,使调用这个实例可以改变直接改变对象状态。

class A:
    def __call__(self, *args, **kwargs):
        print("调用call方法")


a = A() # a是一个对象
print(dir(a))
"""
['__call__', '__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__']
"""
a()
"""
调用call方法
"""

# 斐波那契数列
class Fibonacci(object):

    def __call__(self, num):
        a, b = 1, 1
        self.lst = []

        if num <= 2:
            self.lst.append(a)
            self.lst.append(b)
        else:
            for i in range(1, num + 1):
                self.lst.append(a)
                a, b = b, a + b
        return self.lst


fibo = Fibonacci()
ret = fibo(10)
print(ret)
"""
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
"""


def fibonacci(num):
    a, b = 1, 1
    lst = []

    if num <= 2:
        lst.append(a)
        lst.append(b)
    else:
        for i in range(1, num + 1):
            lst.append(a)
            a, b = b, a + b
    return lst

print(fibonacci(10))

"""
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
"""
# 这两段程序是一样的, 对于类里面的call的逻辑也是就相当于是写在函数里面的逻辑。

# __new__:实例化第一个被调用的方法,至少传一个cls参数,返回一个对象实例
# __init__:实例化第二个被调用的方法,至少传一个self参数,没有返回值
#作用:
    __new__:就是为了给对象在被实例化的时候, 分配一块内存地址。
    __init__:就是给分配的到的地址里面填内容,就是赋值的过程,也叫属性初始化。
注意:实例化对象是Object类底层实现的,其他类要继承Object的__new__方法才能实现实例化对象

class Earth:
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):  # 判断这个属性是否存在
            cls._instance = super().__new__(cls)  # 如果不存在那么就新建一个地址给他
        return cls._instance  # 如果存在,就把现有的地址给他

    def __init__(self, value1, value2):  # 这个方法是一个传值的过程,就是给地址里面写内容,通过上面这个__new__方法使地址永远都只有一个
        self.value1 = value1
        self.value2 = value2
        
a = Earth(1,2)
print(a.value1, a.value2)
""" 1 2 """
b = Earth("a", "b")
print(b.value1, b.value2)
""" a b """
print(a.value1, a.value2)
""" a b """
结论:这样写每次都保存最后一次实例化的值

====================================================================

class Earth:
    instance = None

    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

    def __init__(self, value1, value2):
        self.value1 = value1
        self.value2 = value2


c = Earth('11', '22')
print(Earth.instance)
""" <__main__.Earth object at 0x0000023E4A8F0B48> """
print(c.value1, c.value2)


d = Earth('aa', 'bb')
print(Earth.instance)
""" <__main__.Earth object at 0x0000023E4A8F0B48> """
print(d.value1, d.value2)
""" aa bb """
print(c.value1, c.value2)
""" aa bb """

两个方法都一样:第二个方法好些

#__del__:当一个对象在内存中被销毁的时候会自动触发这个方法
# 这个方法至少接收一个self参数,无返回值
注意:程序会自动调用这个方法,不需要手动执行

class Person(object):

    def __init__(self):
        print('init')

    def __del__(self):
        print('销毁了。。。')


person = Person() # 只要则个程序执行完了,系统系统就会自动去执行__del__
""" init """
""" 销毁了。。。 """


# __str__:字符串显示,至少传递一个self参数,优先调用它
# __repr__:字符串显示,至少传递一个self参数,当没有__str__的时候才会调用__repr__
# 当没有自定义上面的方法的时候,就回去默认执行内置的__str__方法
注意:自定义方法的优先级高于内置方法的优先级,因为每个方法里面都有内置的打印函数,当有自定义的时候,以自定义的优先执行,这也叫重写(优先调用子类方法,如果没有再去调用父类的方法)

class Person(object):

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

    def __repr__(self):
        info = '调用__repr__方法:%s' % (self.value)
        return info


person = Person('aa')
print(person) # 当没有自定义__str__的时候,就调用自定义的__repr__
"""调用__repr__方法:aa"""

class Person(object):

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

    def __str__(self):
        info = '调用__str__方法:%s' % (self.value)
        return info

    def __repr__(self):
        info = '调用__repr__方法:%s' % (self.value)
        return info


person = Person('aa')
print(person) # 当两个都重写了,那么就优先调用__str__方法
"""调用__str__方法:aa"""

class Person(object):

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


person = Person('aa')
print(person)  # 如果都没有自定义,那么就会默认去调用内置的__str__方法
"""<__main__.Person object at 0x000002666FA79888>"""