python基础:常用魔术方法

1.del
销毁魔术方法
触发时机:当一个对象在内存中被销毁的时候自动执行
参数:至少有一个self,接收对象
返回值:无
作用:在对象销毁的时候做一些操作
注意:程序自动调用此方法,不需要我们手动调用。

class Person(object):

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

    # 当没有对象引用的时候,自动执行
    def __del__(self):
        print('销毁了...')


person = Person()
del person  # 手动销毁内存中的对象
print('程序结束了...')
# print(person)  # 报错,找不到person对象

运行结果:

init了...
销毁了...
程序结束了...

2、call
call():可以让类的实例具有类似于函数的行为,
进一步模糊了函数和对象之间的概念。
使用方式:
对象后面加括号,触发执行。即:对象() 或者 类()()

class Person(object):
    def __call__(self, *args, **kwargs): # 参数可传可不传
        print('call...')


person=Person() # 将Person()的内存地址赋值给person
person()    # 内存地址
Person()()  # 内存地址

Person()(10)

运行结果:

call...
call...
call...

练习 使用__call__方法实现斐波那契数列

class Fibonaqi():
    def __call__(self, num):
        a=1
        b=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=Fibonaqi()
a=int(input('请输入要取得斐波那契数的个数:'))
ret=fibo(a)
print(ret)

运行结果:

请输入要取得斐波那契数的个数:5
[1, 1, 2, 3, 5]

3.repr
repr():改变对象的字符串显示

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

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

     class Person(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
    
      # 如果没有__str__的时候,会执行__repr__方法
      # 如果有就不执行__repr__方法
      def __repr__(self):
          msg='name--->{},age--->{}'.format(self.name,self.age)
          return msg
    
    
    
     person=Person('zs',20)
      print(person)
      s1='hello'
      print('i Say %s' % s1)  # i Say hello
      print('i Say %r' % s1)  # i Say 'hello' # 多添加了一对单引号
      print('i Say %s' % person)   # i Say name:zs,age:20 # 调用__str__方法
      print('i Say %r' % person)   # i Say name--->zs,age--->20 # 调用__repr__方法
      # 使用内置repr()方法,默认调用对象的__repr__方法
      print(repr(person))  # 默认调用__repr__方法  # name--->zs,age--->20
    

运行结果:

name:zs,age:20
i Say hello
i Say 'hello'
i Say name:zs,age:20
i Say name--->zs,age--->20
name--->zs,age--->20

4.new
实例化魔术方法
触发时机: 在实例化对时触发
参数:至少一个cls 接收当前类
返回值:必须返回一个对象实例
作用:实例化对象
注意:实例化对象是Object类底层实现,其他类继承了Object的__new__才能够实现实例化对象。
没事别碰这个魔术方法,先触发__new__才会触发__init__

class Person(object):

    # 初始化
    def __init__(self):
        print('init...')

    # 实例化方法(构造方法) ---->创建对象,如果没有此方法,将会默认调用父类的__new__方法
    #
    def __new__(cls, *args, **kwargs):
        print('new...')
        ret = super().__new__(cls) # 调用父类的__new__方法创建对象,然后用返回值来接收
        # print(ret)
        return ret   # 将对象返回给person


person = Person()
# print(person)
print(id(person))

person1=Person()
print(id(person1))

运行结果:

new...
init...
35706864
new...
init...
35706976

4.Python中的比较is和 ==
is 比较两个对象的 id 值是否相等,是否指向同一个内存地址;
== 比较的是两个对象的内容是否相等,即内存地址可以不一样,内容一样就可以了。
默认会调用对象的 eq()方法。继承自 object 的 eq 方法比较两个对象的id

简单举例;

lst1=['a','b','c']
lst2=['a','b','c']
print(lst1==lst2)  # True 比较的是值
print(id(lst1))   # 31702216
print(id(lst2))   # 31728648
print(lst1 == lst2)  # True  比较的是内容
print(lst1 is lst2)  # False 比较的是内存地址

运行结果;

True
31702216
31728648
True
False



per1=Person('zs',10)
per2=Person('zs',10)
print(per1.__dict__)  # {'name': 'zs', 'age': 10}
print(per2.__dict__)  # {'name': 'zs', 'age': 10}
print(per1 == per2)   #  True
print(per1 is per2)   #  False

lst=[]
per1=Person('zs',10)
per2=Person('zs',10)
lst.append(per1)
# in: 底层调用的是==
if per2 not in lst:
    lst.append(per2)

else:
    print('已经有了')

运行结果:

{'name': 'zs', 'age': 10}
{'name': 'zs', 'age': 10}
True
False
已经有了

通过在添加对象方法__eq__方法来返回比较的内容,进而就可以进行 == 的比较。

class Person(object):

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

    # 在父类__eq__方法中比较,默认返回id值,在这里复写,返回的是
    # 实例对象的属性,以字典的形式返回
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

per1 = Person('zs', 10)
per2 = Person('zs', 10)
print(per1.__dict__)  # {'name': 'zs', 'age': 10}
print(per2.__dict__)  # {'name': 'zs', 'age': 10}
print(per1 == per2)  # True
print(per1 is per2)  # False

5.hash
哈希(hash)也翻译作散列。Hash算法,是将一个不定长的输入,通过哈希函数变换成一个定长的输出,即哈希值。
这种哈希变换是一种单向运算,具有不可逆性即不能根据哈希值还原出输入信息。常见的hash算法有:SM3、MD5、SHA-1等 。
Hash主要应用在数据结构以及密码学领域。
在不同的应用场景下,hash函数的选择也会有所侧重。比如在管理数据结构时,主要要考虑运算的快速性。
在python中有内置的哈希函数hash(),返回一个对象(数字、字符串,不能直接用于 list、set、dictionary)的哈希值。示例代码如下:
s = ‘hello’
ret = hash(s)
print(ret)

运行结果:

2375505071164695267

在python中set集合要求数据类型是可哈希的,因为set集合会默认调用对象的__hash__函数进行快速查询,如果找到了则调用对象的__eq__判断两个是是否相同,如果相同则不添加。
保证数据的唯一性(自动去重功能)。
dict 数据结构的key必须是可哈希的,因为dict是无序的因此通过key的hash算法来快速查询,节约时间。

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

person1 = Person('zs', 32)
person2 = Person('ls', 32)
person3 = Person('ww', 32)

set1 = {person1, person2, person3}
print(set1)    # 打印的是set1的内存地址  #  {<__main__.Person object at 0x00000000023CD860>, <__main__.Person object at 0x00000000023CD898>}
# 默认object.__hash__()算出来的值是,id值的1/16
print(id(person1)/hash(person1))      # 16.0
# 内置方法默认调用对象的__hash__()方法
print(id(person1))      # 35637232
print(hash(person1)) # 2227327
print(id(person1) / hash(person1))   #  16.0

1.自定义对象能不能添加到集合中呢?能默认调用父类的__hash__和__eq__
2.object的 hash 值是怎么算的呢,id 是hash 的16倍
3.自定义对象添加到集合中,一般认为两个对象的属性值相同就是同一个对象
–自定义计算规则。
4.注意,如果只定义了__eq__方法,没有定义__hash__方法,__hash__方法会隐式设置成None

在自定义类中,如果没有实现__eq__()和__hash__()方法,会继承object的__eq__()方法和__hash__()方法。

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


    # 如果只定义了__eq__方法,没有定义__hash__方法,那么就是不可哈希类型
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

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



person1 = Person('zs', 32)
person2 = Person('zs', 32)
# 去重原理。先根据哈希值快速查找,__hash__() --->hash值 --->id()
# id不同,哈希值就不同
# 需求:只要是对象的属性值相同.就认为是一个对象,就不让添加
# person1--->hash值
set1={person1}
set1.add(person2)  # person1和person2内容相同,id不同,根据集合去重原理,在__eq__方法和__hash__方法同时创建的情况下,会检测到他们两个的哈希值不同,但是内容相同,将不会添加person2,将只打印person1的内存地址.
print(set1)
# print(id(person1)/ha sh(person1))

运行结果:

{<__main__.Person object at 0x00000000021CD860>}

猜你喜欢

转载自blog.csdn.net/weixin_44239541/article/details/86516262