Python常见的特殊方法

首先来个大体的总览:

1.字符串,字节序列:__repr__、__str__、 __format__、 __bytes__

2.数值转换:__abs__、__bool_、__complex__、__init__、__float__、__hash__、__index__

3.集合模拟:__len__, __getitem__, __setitem__, __delitem__, __contains__

4.可调用模拟:__call__

5.迭代枚举:__iter__, __reversed__, __next__

6.实例创建和销毁:__init__, __new__, __del__

7.属性管理:__getattr__, setattr__, __delattr__, __dir__

8.属性描述符:__prepare__

 

关键方法讲解:

1.__init__(): 这是在类中最为常见的初始化方法,用于初始化实例。

class B(object):
    def __init__(self):
        print("this is B")

b = B()

2.__new__(cls): 这是在新式类中才有的,它的一个参数是代表了需要实例化的类,可以使前面声明过的类。

class A(object):
    def __init__(self, a):
        print("this is B")
    
    def __new__(cls, *args, **kwargs):
        print("this is A __new__")
        return object.__new__(B, *args, **kwargs)
        # return object.__new__(cls, *args, **kwargs) 等同于
        # return object.__new__(A, *args, **kwargs)

a = A()

print(type(a))

# 结果:
# this is A __new__
# <class '__main__.B'>
  • __new__执行在__init__之前,并且__new__决定是否使用本类的__init__,它可以选择调用其他类的构造方法。
  • 如果new返回的不是本类,那么就不会调用该类的构造方法

3.__repr__ 和 __str__:这两种特殊方法基本相似,首先Python原文说一个是给机器看的一个是给人看的,看来网上好多人写的都没咋看明白,我就说说自己的理解。在经过多重测试之下得出一下结论:如果类中同时重写了__str__ 和__repr__,那么通过print()这个函数输出的类会调用str,如果只有repr则会执行repr。                                                                                                          上面得到的是结论,接下来理解一下。打印操作会首先尝试__str__和str内置函数(print运行的内部等价形式),它通常应该返回一个友好的显示。__repr__用于所有其他的环境中:用于交互模式下提示回应以及repr函数,如果没有使用__str__,会使用print和str。它通常应该返回一个编码字符串,可以用来重新创建对象,或者给开发者详细的显示。

class A():
    def __init__(self):
        return 0

    def __str__(self):
        print("this is str")
        return "str"

    def __repr__(self):
        print("this si repr")
        return "repr"
a = A()
print(a)  # 直接输出a会调用__str__      
print(repr(a))  # 通过repr()函数执行类中的__repr__
# 输出
# this is str
# str
# this is repr
# repr

4.__call__: 这个函数的主要作用类似于C++中的重载了括号运算符"()", 当对象后面加一个括号后就会执行这个方法,将类实例看做函数。

class A(object):
    def __init__(self):
        print("this is A")

    def __call__(self, *args, **kwargs):
        print("this is call")

        return "1234456"

a = A()
a()
# 输出:
# this is call

5. __getattr__, __setattr__, __delattr__:如果类重写了getattr,那么通过实例对属性访问时的查找顺序是先__dict__ ,如果dict中不存在这个属性,然后访问 getattr,也就是说getattr是在属性找不到的情况下才会执行。

    如果类自定义了__setattr__方法,当通过实例获取属性尝试赋值时,就会调用__setattr__。常规的对实例属性赋值,被赋值的属性和值会存入实例属性字典__dict__中。delattr的用法和setattr一样

class A(object):
    def __init__(self, name):
        print("this is __init__")
        self.name = name

    def __getattr__(self, item):
        self.name = item
        return ("this is getattr " + item)

    def __setattr__(self, key, value):
        print("set " + key + " = " + value)
        self.__dict__[key] = value
    
    def __delattr__(self, key):
        return object.__delattr__(self, key)

a = A("liixn")  # 在__init__中对name赋值时会调用__setattr__
a.name = "wang"  # 通过a.name对属性复制还是会调用__setattr__
a.score = '88'  # 通过对不存在dict中的属性复制也是可行的
print(a.id)     # 查找不存在于dict中的属性会调用__getattr__
print(a.__dict__)   # 查看dict

del a.score
print(a.__dict__)

输出如下:

this is __init__
set name = liixn
set name = wang
set score = 88
set name = id
this is getattr id
{'name': 'id', 'score': '88'}
{'name': 'id'}

6.__len__:一个类的表现类似域列表的时候,可以编写len来返回有多少个元素。

class A(object):
    def __init__(self, *args):
        print("this is __init__")
        self.L = args

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

a = A('1', '2', '3')
print(len(a))  # 3

7.__getitem__:通俗的来讲就是实现了(P设为实例) P[key] 这种形式的访问                                            ,当实例通过P[key]取值时,就会调用__getitem__

注意当访问不存在属性的时候才会调用 __getitem__    

class A(object):
    def __init__(self, dic):
        print("this is __init__")
        self.L = dic

    def __getitem__(self, item):
        print("this is getitem")
        return self.L[item]  # return 可有可无
dic = {'1': 1, '2': 2, '3':3 }
a = A(dic) 
print(a["1"])

输出:
this is __init__
this is getitem
1

8.__setitem__:每当实例以这种形式复制时P[key] = value 都会调用该方法,就算类中不存在某个key,也可以通过这种方式复制,通过查看__dict__可以看到这个key会被新添加。

class A(object):
    def __init__(self):
        self['a'] = 'aa'
        self['b'] = 'bb'

    def __getitem__(self, item):
        print("this is getitem")
        return self.__dict__[item]

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

a = A()
print(a['a'])  # 打印原始的'a'
print(a.__dict__)  # 查看dict
a['a'] = 'a1a1'  # 修改'a'的值
a['c'] = 'cccc'  # 不存在'c'
print(a['c'])   # 打印'c'
print(a.__dict__)  # 再次查看dict

# 输出
# aaaa
# {'a': 'aaaa', 'b': 'bbbb'}
# cccc
# {'a': 'a1a1', 'b': 'bbbb', 'c': 'cccc'}

9.__delitem__:在对对象使用del P[key]时调用,使用方式大致如下。如果不存在key 则会报错

class A(object):
    def __init__(self):
        self['a'] = 'aaaa'
        self['b'] = 'bbbb'
    def __delitem__(self, key):
        del self.__dict__[key]
a = A()
print('修改前:', a.__dict__)  # 查看dict
del a['a']
print('修改后:', a.__dict__)  # 再次查看dict

# 修改前: {'a': 'aaaa', 'b': 'bbbb'}
# 修改后: {'b': 'bbbb'}

10.__iter__与__next__: (这是实现生成器的关键函数)首先我们知道一个普通的实例对象是不具备可迭代功能的,如果你想一个类的实例对象具有list 这类可迭代功能的要求,那么就需要实现一个__iter__以及__next__这个方法。我们可以通过一下方式访问:

class A():
    def __init__(self,t):
        self.t = t
    def __iter__(self):
        return self
    def __next__(self):
        if self.t>5:
            raise StopIteration 
        else:
            self.t += 1
            return self.t

for i in A(3):
    print(i)

# 输出
# 4
# 5
# 6

值得注意的是含有__next__()函数的对象都是一个迭代器,如果没有重写__iter__但是重写了__next__,我们依然可以换种方式对对象遍历:

class A(object):
    def __init__(self, data=1):
        self.data = data    
    def __next__(self):
        if self.data > 5:
            raise StopIteration
        else:
            self.data += 1
            return self.data
a = A()
for i in range(3):
    print(a.__next__())

# 2
# 3
# 4