一、跟实例创建和执行有关的
__new__、__init__、__call__.
类加括号调用了__init__方法来创建一个实例对象。这一过程分成了两步: 类调用__new__来创建实例对象,__new__调用__init__来初始化实例对象。
class A: count = 0 def __init__(self): print("__init__ has called. 2") def __new__(cls): print("__new__ has called. 1") return object.__new__(cls) def __call__(cls): print("__call__ has called. 3") a = A() a() """ __new__ has called. 1 __init__ has called. 2 __call__ has called. 3 """
类的__new__()方法很少通过用户代码定义。如果定义了它,它通常是用原型__new__(cls, *args, **kwargs)编写的。其中args和kwargs与传递给__init__()的参数相同。__new__()始终是一个类方法,接受类对象作为第一个参数。尽管__new__()会创建一个实例,但它不会自动地调用__init__()。实例对象加括号会调用__call__方法,一般作为程序的入口,并且可以在这一步修改实例属性或调用实例方法。
class Obj(object): def __init__(self, name, price): self.name = name self.__price = price def __say(self): print("{}, {}.".format(self.name, self.__price)) def __call__(self, discount): self.__price = discount * self.__price self.__say() apple = Obj("apple", 10.0) apple(0.8)
二、跟实例属性有关的
__getattribute__, __getattr__, __setattr__, __delattr__、__getitem__、__setitem__、__delitem__、__missing__
在__init__时,会调用__setattr__初始化实例属性,在__dict__添加键值对。
class Sample: def __init__(self, name, age): self.name = name self.age = age def __setattr__(self, key, value): print("__setattr__ has called.") try: self.__dict__[key] = value except: self.__dict__ = {key: value} sam = Sample("Li", 24) # __setattr__ has called. # __setattr__ has called.
对实例属性的访问时,
1.首先执行__getattribute__方法(这又是一个特殊方法),去调用object基类的__getattribute__来查询__dict__里的键值对。如果存在则直接返回,相当于执行了member.__dict__.get(obj),如果不存在则调用__getattr__方法。
2.__getattr__方法会在实例内存空间中尽可能的搜索该属性值,如果搜索到则直接返回,搜索不到会抛出AttributeError。
注意下面两段代码的区别:__getattribute__的return只是一个硬编码的字符串而不是属性值,__getattr__的return则是实例的属性值。因此,如果有需求,一般会在__getattr__里写需求代码。
class MemberCounter: def __init__(self, name, age): self.name = name self.age = age def __getattribute__(self, obj): print("__getattribute__ is called.") return object.__getattribute__(self, obj) def __getattr__(self, obj): print("__getattr__ is called.") return "{} Not assgined.".format(obj) member = MemberCounter("An", 24) print(member.name) print(" ------------------ ") print(member.gender) """ __getattribute__ is called. An ------------------ __getattribute__ is called. __getattr__ is called. gender Not assgined. """
class MemberCounter: def __init__(self, name, age): self.name = name self.age = age def __getattribute__(self, obj): if obj == "gender": return "male" else: return object.__getattribute__(self, obj) member = MemberCounter("An", 24) print(member.gender) member.gender = "female" print(member.gender) print(member.__dict__) """ male male {'name': 'An', 'age': 24, 'gender': 'female'} """
class MemberCounter: def __init__(self, name, age): self.name = name self.age = age def __getattr__(self, obj): if obj == "gender": return "male" else: raise AttributeError member = MemberCounter("An", 24) print(member.gender) member.gender = "female" print(member.gender) print(member.__dict__) """ male female {'name': 'An', 'age': 24, 'gender': 'female'} """
当访问一个属性的时候:
解释器首先在实例的字典中搜索,
若找不到则去创建这个实例的类的字典中搜索,
若还找不到就到类的基类中搜索,
如果还不找不到最后会尝试调用类的__getattr__方法来获取属性值(若类中定义了该方法的话).
如果这个过程也失败,则引发AttributeError异常
在使用member.name = "Wei"和del member.age时,实际上调用了__setattr__和__delattr__。这两个函数没有类似__getattribute__的使用规则。即无论何时给属性赋值,都会调用 __setattr__()
方法;无论何时删除一个属性,都将调用 __delattr__()
方法。
class MemberCounter: def __init__(self, name, age): self.name = name self.age = age def __setattr__(self, old, new): print("