python魔术方法

本文详细介绍了Python的魔术方法,包括常规魔术方法如`__init__`和`__del__`,比较方法如`__eq__`和`__ne__`,运算符重载方法如`__add__`和`__sub__`,属性访问控制的`__getattr__`和`__setattr__`,以及序列和可调用对象的魔术方法。通过这些魔术方法,可以自定义对象的行为,实现类的特殊功能,如序列化和文件打开器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常规:

__del__魔术方法:

对象即将被释放的时候,会调用这个方法。注意如果这个对象产生了循环引用,并且实现了__del__方法,那么这个对象将得不到释放,从而产生内存泄漏。因此慎用这个方法。

__new__魔术方法:

这个方法用来创建对象。如果想在创建对象的时候做些事情,那么可以重写这个方法。

__class__魔术属性:

用来返回这个对象所属的。如果一个类调用这个属性,那么得到的是这个类的元类

__iter__魔术方法:

返回一个迭代器。

__next__魔术方法:

迭代器在被遍历的时候会每遍历一次调用这个方法(在Python3中,在Python2中使用的是next方法)

__str__魔术方法:

  1. 在打印某个对象的时候,会调用这个对象的__str__方法,打印这个方法的返回值。
class Person(object):
    def __init__(self,name):
        self.name = name
p1 = Person('zhouxin')
print(p1)
# 打印出来的结果:
# <__main__.Person object at 0x008AD170>

而重写了__str__方法,那么打印出来的就是__str__的返回值:

class Person(object):
    def __init__(self,name):
        self.name = name
    def __str__(self):
        return 'Person<%s>' % self.name
p1 = Person('zhouxin')
print(p1)
# 打印出来的结果:
# Person<zhouxin>
  1. 如果在使用str(obj)这个函数的时候,也会调用__str__方法。

__repr__魔术方法:

  1. __repr__魔术方法是用来表述某个对象在内存中的展示形式
  2. 如果在终端直接输入一个对象,然后按回车,那么将会执行这个对象的__repr__方法。
  3. 如果你将几个对象扔到一个容器中(比如:列表),那么你在打印这个容器的时候,会依次调用这个容器中的元素的__repr__方法。如果没有实现这个__repr__方法,那么得到的将是一个类名+地址的形式,这种形式的是不好理解的。
class Person(object):
    def __init__(self,name):
        self.name = name

    def __repr__(self):
        return "Person(%s)" % self.name

p1 = Person('zhouxin1')
p2 = Person('zhouxin2')
a = [p1,p2]
print(a)
# 打印的结果:
[Person(zhouxin1), Person(zhouxin2)]

__dict__魔术属性:

  1. 用来获取用户自定义的属性,以及这个属性对应的值。返回的是一个字典
  2. dir函数做一个区分。dir函数返回的是这个对象上拥有的所有属性,包括Python内置的属性和用户自己添加的,并且只是获取属性名字,不会获取这个属性对应的值。

比较:

__cmp__魔术方法:

__cmp__(self,other)这个方法在Python2中可以正常使用。如果self是大于other的,那么应该返回正数,如果self等于other,那么应该返回0,如果self小于other,那么应该返回负数。
这个方法只能在Python2中起作用,在Python3中不再有用了。

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

    def __cmp__(self, other):
        if self.age == other.age:
            if self.height == self.height:
                return 0
            else:
                return 1 if self.height > other.height else -1
        return 1 if self.age > other.age else -1

__eq__(self,other)

比较两个对象是否相等,如果相等,那么返回True,否则返回Flase

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

    def __eq__(self, other):
        # self == other
        if self.age == other.age:
            if self.height == other.height:
                return True
            else:
                return True if self.height == other.height else False

__ne__(self,other)

如果两个对象不想等,那么返回True,否则返回Flase

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

    def __ne__(self, other):
        # self != other
        return False if self.age == other.age and self.height == other.height else True

__lt__(self,other)

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

    def __lt__(self, other):
        # self < other
        if self.age == other.age:
            return True if self.height < other.height else False
        return self.age < other.age

如果self小于other,那么应该返回True,否则返回False

__gt__(self,other)

如果self是大于other,那么应该返回True,否则返回Flase

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

    def __gt__(self, other):
        # self > other
        if self.age == other.age:
            return True if self.height > other.height else False
        return self.age > other.age

__ge__(self,other)

如果self>=other,那么应该返回True,否则返回Flase

__le__(self,other)

如果self<=other,那么应该返回True,否则返回Flase

__ge____le__注意点:
  1. 这两个方法在Python2中写不写都无所谓。因为她会分开来判断。比如<=,那么首先会判断是否<,然后在判断是否等于。
  2. 在Python3中,必须要实现这两个方法中的一个。当然最好两个都实现。

运算符:

一元运算符魔术方法:

  1. __pos__(self)魔术方法:在对象前面使用+的时候会调用。
  2. __neg__(self)魔术方法:在对象前面使用-的时候会调用。
  3. __abs__(self)魔术方法:在给对象执行abs(obj)的时候会调用。
  4. __invert__(self)魔术方法:在对象前面使用~的时候会调用。
  5. 如果在这些魔术方法中,所有的更改都是在self对象上,那么将会更改这个对象本身。如果是新建一个对象,再更改这个对象的值,那么更改的是这个对象的副本。
class Coornidate(object):  
    def __init__(self,x,y):  
        self.x = x  
        self.y = y

    def _pos__(self):  
        return self

    def __neg__(self):  
        new_coornidate = Coornidate(-self.x,-self.y)  
        return new_coornidate

    def __abs__(self):  
        new_coornidate = Coornidate(abs(self.x),abs(self.y))  
        return new_coornidate

    def __invert__(self):  
        new_coornidate = Coornidate(255-self.x,255-self.y)  
        return new_coornidate

    def __str__(self):  
        return "(%s,%s)" % (self.x,self.y)

c1 = Coornidate(-1,2)  
c2 = +c1  
c3 = -c1  
c4 = abs(c1)  
c5 = ~c1  
print(c5)

二元运算符魔术方法:

  1. __add__(self,other):两个对象相加的时候会调用。
  2. __sub__(self,other):两个对象相减的时候会调用。
  3. __mul__(self,other):两个对象相乘的时候会调用。
  4. __div__(self,other):只有在Python2中才有用,两个对象相除的时候会调用。
  5. __truediv__(self,other):两个对象真正相除的时候会调用。如果在Python2中,必须从Python3中导入division这个特性。也就是from __future__ import division
  6. __mod__(self,other):两个对象取模操作的时候会调用。
class Coornidate(object):
     def __init__(self,x,y):
         self.x = x
         self.y = y

     def __add__(self, other):
         new_coornidate = Coornidate(self.x+other.x,self.y+other.y)
         return new_coornidate

     def __sub__(self, other):
         new_coornidate = Coornidate(self.x-other.x,self.y-other.y)
         return new_coornidate

     def __mul__(self, other):
         new_coornidate = Coornidate(self.x*other.x,self.y*other.y)
         return new_coornidate

     def __floordiv__(self, other):
         new_coornidate = Coornidate(self.x//other.x,self.y//other.y)
         return new_coornidate

     def __div__(self, other):
         new_coornidate = Coornidate(self.x/other.x,self.y/other.y)
         return new_coornidate

     def __truediv__(self, other):
         new_coornidate = Coornidate(self.x/other.x,self.y/other.y)
         return new_coornidate

     def __mod__(self, other):
         new_coornidate = Coornidate(self.x%other.x,self.y%other.y)
         return new_coornidate

     def __str__(self):
         return "(%s,%s)" % (self.x,self.y)

 c1 = Coornidate(4,6)
 c2 = Coornidate(3,4)
 c3 = c1+c2
 c4 = c1 - c2
 c5 = c1*c2
 c6 = c1/c2
 c7 = c1%c2
 print(c7)

属性访问控制:

__getattr__魔术方法:

在访问一个对象的某个属性的时候,如果这个属性不存在,那么就会执行这个方法。

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

    def __getattr__(self, item):
        if item == 'age':
            return 18
        else:
            return None

p1 = Person('zhouxin')
print(p1.name)
print(p1.age)

__setattr__魔术方法:

给一个对象设置属性的时候,就会执行这个魔术方法。要注意的是,使用self.__dict__[key]=value的形式,避免使用self.xxx=xxx产生死循环的递归。

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

    def __setattr__(self, key, value):
        if key == 'age':
            self.__dict__[key] = value
            if value >= 18:
                self.__dict__['is_adult'] = True
            else:
                self.__dict__['is_adult'] = False
        else:
            self.__dict__[key] = value

__getattribute__魔术方法:

在访问一个对象的属性的时候,都会执行这个魔术方法。要注意的是,使用super(类名,self).__getattribute__(item)来避免产生死循环的递归。

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

    def __getattribute__(self, item):
        print(item)
        return super(Person, self).__getattribute__(item)

__getattr____getattribute__区别:

  1. __getattr__只有属性不存在的时候才会调用。
  2. __getattribute__不管属性存不存在都会调用。

创建属于自己的序列

__len__()魔术方法:

在使用len(obj)的时候,会调用这个魔术方法。

__getitem__(self,key)魔术方法:

使用下标或者切片操作获取值的时候会调用这个魔术方法。
示例:

my_list = ZLList()
temp = my_list[0:2]

__setitem__(self,key)魔术方法:

使用下标或者切片操作设置值的时候会调用这个魔术方法。
示例:

my_list = ZLList()
my_list[0:2] = ['a','b']

__delitem__(self,key)魔术方法:

使用del关键字执行下标或者切片操作删除值的时候会调用这个魔术方法。
示例:

my_list = ZLList()
del my_list[0]

__iter__()魔术方法:

使用for循环遍历这个序列的时候会调用这个方法。
示例:
示例:

my_list = ZLList()
for x in my_list:
    print(x)

__reversed__魔术方法:

在使用reversed(obj)函数的时候,会执行这个魔术方法。

class ZLList(object):
 def __init__(self,values=None):
     if values is None:
         self.values = []
     else:
         self.values = values

 def __iter__(self):
     return iter(self.values)

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

 def __getitem__(self, item):
     return self.values[item]

 def __setitem__(self, key, value):
     self.values[key] = value

 def __delitem__(self, key):
     del self.values[key]

 def __reversed__(self):
     return reversed(self.values)

 def append(self,value):
     self.values.append(value)

 def remove(self,value):
     return self.values.remove(value)

 def head(self):
     return self.values[0]

 def tail(self):
     return self.values[-1]

 def take(self,n):
     return self.values[:n]

可变容器可以对容器中的元素进行更改,比如删除一个元素,添加一个容器。而不可变容器不能做这些操作。因此如果想定义一个不可变容器,那么不应该实现__setitem____delitem__方法。

魔术方法(可调用的对象)

__call__魔术方法:

如果想让一个对象,能够想函数那样被调用,例如:index_view(),那么应该实现__call__方法。

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

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

    def __str__(self):
        return '(%s,%s)' % (self.x,self.y)

c1 = Coornidate(1,2)
print(callable(c1))
c1(2,3)
print(c1)

魔术方法(会话管理)

__enter__魔术方法:

使用with语句的时候,会调用这个魔术方法,这个方法的返回值可以作为as xx的值。
示例:

with open('xxx.txt','r') as fp:
    print(fp.read())

__exit__(self,exc_type,exc_val,exc_tb)魔术方法:

  1. 执行完这个with语句中的代码块或者是这个代码块中的代码发生了异常,就会执行这个方法。可以在这个方法中做一些清理工作。比如关闭文件等。
  2. 如果在with语句中发生了异常,那么exc_typeexc_val将会存储这个异常的信息,如果没有任何异常,那么他们的值为None
  3. 如果在with语句中发生了异常,那么会执行__exit__方法,但是如果你不想让这个异常抛出with代码块,那么你可以返回True,就不会把异常抛出到外面了。

自定义文件打开器

class FileOpener(object):
    def __init__(self,*args,**kwargs):
        self.args = args
        self.kwargs = kwargs

    def __enter__(self):
        self.fp = open(*self.args,**self.kwargs)
        return self.fp

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.fp.close()
        print(exc_type)
        return False


with FileOpener('test.txt','r') as fp:
    a = 1
    b = 0
    # c = a/b
    print(fp.read())

对象持久化

有时候一个对象,想要保存在硬盘中。这时候就需要使用到对象持久化的技术了。在Python中,如果要将一个对象存储到硬盘中,需要使用pickle模块,其中dump方法可以将一个对象存到硬盘中,load方法可以从硬盘中加载一个对象。以下举几个例子来介绍下序列化的大体步骤:

  1. 写入对象到磁盘中:
import pickle

 data = {'foo': [1, 2, 3],
         'bar': ('Hello', 'world!'),
         'baz': True}
 jar = open('data.pkl', 'wb')
 pickle.dump(data, jar) # 写入数据到文件中
 jar.close()
  1. 从硬盘中加载对象:
 import pickle

 pkl_file = open('data.pkl', 'rb') # connect to the pickled data
 data = pickle.load(pkl_file) # load it into a variable
 print data
 pkl_file.close()

Python内置的数据结构是直接可以被序列化的:

字典、元组、列表、集合、字符串、整形、浮点型、布尔类型。

如果自己定义的对象想要能够被序列化,那么必须实现以下两个魔术方法:

  1. __getstate__:当这个对象被序列化的时候,会调用这个方法,然后将这个方法的返回值存储到硬盘中。这个方法的返回值必须是可序列化的。
  2. __setstate__:当从硬盘中加载一个对象的时候,会调用这个方法,并且将加载到的数据作为参数传递给这个方法。拿到这个数据后,就可以做自己的事情了,比如将之前的数据重新保存到这个对象上。
import pickle

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

    def __getstate__(self):
        return {"name":self.name,'age':self.age}

    def __setstate__(self, state):
        self.name = state['name']
        self.age = state['age']

    def __str__(self):
        return "<name:%s,age:%d>" % (self.name,self.age)


def dump_obj():
    p1 = Person('zhiliao',18)
    with open('person.pkl','wb') as fp:
        pickle.dump(p1,fp)

def load_obj():
    with open('person.pkl','rb') as fp:
        p1 = pickle.load(fp)
        print(p1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值