python魔术方法---__del__、__call__、__repr__、__new__等

目录

一、常用魔术方法

1.__del__

2.__call__

3.__repr__

4.__new__

5.Python中的比较is和 ==

6.__hash__


一、常用魔术方法

1.__del__

销毁魔术方法

触发时机:当一个对象在内存中被销毁的时候自动执行

参数:至少有一个self,接收对象

返回值:无

作用:在对象销毁的时候做一些操作

注意:程序自动调用此方法,不需要我们手动调用。

练习1:测试__del__()方法执行的时机

# __del__() 在对象不再被使用的时候,自动销毁
class A(object):

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

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


a = A()
del a  # 销毁对象不是删除内存空间
print('程序结束了')
# print(a)  # NameError: name 'a' is not defined
b = A()
del b
'''
init....
销毁了...
程序结束了
init....
销毁了...
'''

2.__call__

__call__():可以让类的实例具有类似于函数的行为,

进一步模糊了函数和对象之间的概念。

使用方式:

对象后面加括号,触发执行。即:对象() 或者 类()()

class A(object):

    def __call__(self, *args, **kwargs):
        print('call....')


a = A()
a()  # 自动调用call call....

练习1使用__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(num):
                self.lst.append(a)
                a, b = b, a + b
        return self.lst


f = Fibonacci()
print(f(4)) # [1, 1, 2, 3]

3.__repr__

__repr__():改变对象的字符串显示

- 此方法是__str__()的备胎,如果找不到__str__()就会找__repr__()方法。

- %r 默认调用的是 __repr()方法,如果是字符串会默认加上 ''

-repr()方法默认调用__repr__()方法

 

class A(object):

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

    def __str__(self):
        msg = 'name:{},age:{}'.format(self.name, self.age)
        return msg

    def __repr__(self):
        msg = 'name--->{},age--->{}'.format(self.name, self.age)
        return msg


a = A('za', 34)
# 用%r,默认调用__repr__()方法
print('%s' % a)  # name:za,age:34
print('%r' % a)  # name--->za,age--->34
print(a)  # name:za,age:34 # 有__str__()方法就会调用__str__()方法,没有就调用__repr__()方法
print(repr(a))  # name--->za,age--->34 # repr()方法默认调用__repr__()方法

4.__new__

实例化魔术方法

触发时机: 在实例化对时触发

参数:至少一个cls 接收当前类

返回值:必须返回一个对象实例

作用:实例化对象

注意:实例化对象是Object类底层实现,其他类继承了Object的__new__才能够实现实例化对象。

没事别碰这个魔术方法,先触发__new__才会触发__init__

练习1:查看__new__方法的执行时机。

class Person(object):

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

    # 实例化方法-->object类中的new创建对象
    def __new__(cls, *args, **kwargs):  # 重写后,不再创建对象
        print('new...')

per = Person()
print(per)
'''
new...
None
'''
# None说明没有创建对象,因为我们重写了__new__方法没有了创建对象的功能,只有打印

练习2: 调用父类的__new__方法,创建当前对象。

class Person(object):

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

    # 实例化方法-->object类中的new创建对象
    def __new__(cls, *args, **kwargs):  
        print('new...')
        ret = super().__new__(cls)  # 调用父类的__new__方法创建对象
        return ret


per = Person()
print(per)
'''
new...
init...
<__main__.Person object at 0x00000000021FC9B0>
'''

5.Python中的比较is和 ==

is 比较两个对象的 id 值是否相等,是否指向同一个内存地址;

== 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。

默认会调用对象的 __eq__()方法。继承自 object __eq__ 方法比较两个对象的id

引入:lst1和lst2的值相同,但id不同

lst1 = [1, 2, 3]
lst2 = [1, 2, 3]
print(id(lst1)) # 35726664
print(id(lst2)) # 35760008
print(lst1 == lst2) # True
print(lst1 is lst2) # False

重写__eq__()方法前: 

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


p = P('zs', 2)
p1 = P('zs', 2)
print(p.__dict__) # {'name': 'zs', 'age': 2}
print(p1.__dict__) # {'name': 'zs', 'age': 2}
print(p == p1)  # False # p 和 p1的值相同 # ==比较时默认调用object.__eq__方法,默认比较两个对象的地址
print(p is p1)  # False

重写__eq__ ()方法后:

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

    # == 时调用
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

p = P('zs', 2)
p1 = P('zs', 2)
print(p.__dict__) # {'name': 'zs', 'age': 2}
print(p == p1)  # True
print(p is p1)  # False

练习:判断对象是否在lst中,不在的话就加入

p = P('zs', 2)
p1 = P('zs', 2)
lst = []
lst.append(p1)
if p not in lst:
    lst.append(p)
print(lst) # [<__main__.P object at 0x00000000021CC908>]

6.__hash__

哈希(hash)也翻译作散列。Hash算法,是将一个不定长的输入,通过哈希函数变换成一个定长的输出,即哈希值。

这种哈希变换是一种单向运算,具有不可逆性即不能根据哈希值还原出输入信息。常见的hash算法有:SM3、MD5、SHA-1等 。

Hash主要应用在数据结构以及密码学领域。

在不同的应用场景下,hash函数的选择也会有所侧重。比如在管理数据结构时,主要要考虑运算的快速性。

在python中有内置的哈希函数hash(),返回一个对象(数字、字符串,不能直接用于 list、set、dictionary)的哈希值。示例代码如下 :

print(hash('123'))
输出:-4940008903264522313

在python中set集合要求数据类型是可哈希的,因为set集合会默认调用对象的__hash__函数进行快速查询,如果找到了则调用对象的__eq__判断两个是是否相同,如果相同则不添加。

保证数据的唯一性(自动去重功能)。

dict 数据结构的key必须是可哈希的,因为dict是无序的因此通过key的hash算法来快速查询,节约时间。

set = {1, 'v', [1, 2, 3, 32]}
print(set) # unhashable type: 'list'
# 不可哈希的数据类型 内部的__hash__ = None,set集合无法通过
  1. 自定义对象不能添加到集合,能默认调用父类的__hash__和__eq__
  2. object的 hash 值是id 是的1/16
  3. 自定义对象添加到集合中,一般认为两个对象的属性值相同就是同一个对象
  4. 注意,如果只定义了__eq__方法,没有定义__hash__方法,__hash__方法会隐式设置成None,在自定义类中,如果没有实现__eq__()和__hash__()方法,会继承object的__eq__()方法和__hash__()方法。
class People(object):

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


person1 = People('zz', 23)
person2 = People('zs', 23)  # 可哈希
set = {person1, person2}
print(set)
'''
{<__main__.People object at 0x00000000027BC9B0>,
 <__main__.People object at 0x00000000027BC978>}
'''
print(id(person1)/16)
print(hash(person1))
'''
2608283.0
2608283
'''

自定义对象添加到集合中,我们一般认为两个对象的属性值相同就是同一个对象。因此需要我们手动复写__eq__方法和__hash__方法。 

class People(object):

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

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    def __hash__(self):
        return hash(self.name)+hash(self.age)


person1 = People('zz', 23)
person2 = People('zz', 23)
# # 集合去重原理 先根据元素的哈希值快速查找,若没有相同的,然后判断值
set1 = {person1}
set1.add(person2)
print(set1) # {<__main__.People object at 0x00000000021C8748>}

注意,如果只定义了__eq__方法,没有定义__hash__方法,__hash__方法会隐式设置成None 

class People(object):

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

    def __eq__(self, other):
        return self.__dict__ == other.__dict__


person1 = People('zz', 23)
person2 = People('zz', 23)
# # 集合去重原理 先根据元素的哈希值快速查找,若没有相同的,然后判断值
set1 = {person1}
set1.add(person2)
print(set1) # TypeError: unhashable type: 'People'

 

你可能感兴趣的:(IT,python学习之路)