一、跟实例创建和执行有关的
__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("\n------------------\n") 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("\033[1;36m {} = {} \033[0m has been created.".format(old, new)) object.__setattr__(self, old, new) def __delattr__(self, key): print("{} is deleted.".format(key)) del self.__dict__[key] def __getattr__(self, item): return self.__dict__[item] # object.__getattr__(self, item) member = MemberCounter("An", 24) print(member.__dict__) del member.age print(member.__dict__) """ name = An has been created. age = 24 has been created. {'name': 'An', 'age': 24} age is deleted. {'name': 'An'} """
class A: def __init__(self, *args): self.name, self.age = args def __getitem__(self, key): print("__getitem__ has called.") self.__dict__.get(key, ValueError) def __setitem__(self, key, value): print("__setitem__ has called.") self.__dict__.update({key: value}) def __delitem__(self, key): print("__delitem__ has called.") del self.__dict__[key] a = A("Li", 27) print(a.__dict__) a["name"] print(a.__dict__) a["name"] = "Alex" print(a.__dict__) del a["age"] print(a.__dict__) """ {'name': 'Li', 'age': 27} __getitem__ has called. {'name': 'Li', 'age': 27} __setitem__ has called. {'name': 'Alex', 'age': 27} __delitem__ has called. {'name': 'Alex'} """
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
1 | 获取一个计算属性(无条件的) | x.my_property |
x.__getattribute__('my_property') |
2 | 获取一个计算属性(后备) | x.my_property |
x.__getattr__('my_property') |
3 | 设置某属性 | x.my_property = value |
x.__setattr__('my_property', value) |
4 | 删除某属性 | del x.my_property |
x.__delattr__('my_property') |
5 | 列出所有属性和方法 | dir(x) |
x.__dir__() |
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
1 | 通过键来获取值 | x[key] |
x.__getitem__(key) |
2 | 通过键来设置值 | x[key] = value |
x.__setitem__(key, value) |
3 | 删除一个键值对 | del x[key] |
x.__delitem__(key) |
4 | 为缺失键提供默认值 | x[nonexistent_key] |
x.__missing__(nonexistent_key) |
三、实例属性的保存格式---__slots__和__dict__
实例对象通常以字典的形式保存属性, 也即__dict__。这种格式可以被__slots__修改 。下面是从各个python书籍中摘下的关于__slots__描述。
定义__slots__后,可以在实例上分配的属性名称将被限制为指定的名称,否则将引发AttributeError异常。
这种限制可以阻止其他人向现有实例添加新属性,解决了用户将值分配给他们无法正确拼写的属性时出现的异常。
在实际使用时,__slots__从未被当做一种安全的特性来实现。它实际上是对内存和执行速度的一种性能优化。
使用__slots__的类的实例不再使用字典来存储实例数据。相反,会使用基于数组的更加紧凑的数据结构。
在会创建大量对象的程序中,使用__slots__可以显著减少内存占用和执行时间。
__slots__与集成的配合使用需要一定的技巧。如果类继承自使用__slots__的基类,那么它也需要定义__slots__来存储自己的属性(即使它不会添加任何属性)。这样才能充分利用__slots__提供的优势。如果忘记了这一点,派生类的运行速度将更慢,占用的内存也比未在任何类上使用__slots__时多。
__slots__的使用也可以使代码不必要求实例具有底层__slots__属性。尽管这一点通常不适用于用户代码。但可以编写其他支持对象实用工具库和其他工具。依靠__dict__来调试、序列化对象并执行其他操作。
__slots__的存在不会对__getattribute__()、__getattr__()、和__setattr__()等方法的调用产生任何影响。因为这些方法应该在类中重新定义。但是,这些方法的默认行为将考虑到__slots__。
此外,没用必要向__slots__添加方法或特性名称,因为它们存储在类中,而不是存储在每个实例中。
class MemberCounter: __slots__ = ("name", "age") # 注意, ()指定存储格式为元组,"name"和"age"严格限制了实例化对象的参数,不可随意增加,但可以删除 def __init__(self, name, age): # 不可再写*args, **kwargs self.name = name self.age = age member = MemberCounter("An", 24, ) # member.__dict__ # 当__slots__被指定时,覆盖了__dict__ print(member.__slots__) print(member.name, member.age) # member.gender = "female" 报错 #('name', 'age') # An 24age"
如何在定义了__slots__,来储存dict或者list等数据呢?以下面的例子为例:
class Local(object): __slots__ = ('__dict__', '__list__') def __init__(self, name, age, gender): object.__setattr__(self, "__dict__", {}) # 注意,__dict__和__list__只是我随意写的,你可以通篇把__dict__改成dict,照样没有关系 object.__setattr__(self, "__list__", []) # 也就是说,__dict__只是一个属性链接,它从__slots__中链接到一个字典对象并提供相关操作 self.name = name # 假定你已经知道self.name = name 实际上式调用了__setattr__方法,也就是自动存到了self.__dict__里 self.age = age self.gender = gender def __setattr__(self, key, value): try: self.__dict__[key] = value except KeyError: self.__dict__ = {key: value} def __getattr__(self, name): try: return self.__dict__[name] except KeyError: raise AttributeError(name) def __delattr__(self, name): try: del self.__dict__[name] except KeyError: raise AttributeError(name) local = Local("Li", 24, "famle") print(local.__slots__) print(local.__dict__)
# 0('__dict__', '__list__')
# {'name': 'Li', 'age': 24, 'gender': 'famle'
四、跟运算有关的
__eq__(==)、__ge__(>=)、__gt__(>)、__le__ (<=)、__lt__ (<)、__ne__ (!=)、__bool__
参见: http://old.sebug.net/paper/books/dive-into-python3/special-method-names.html
class A: def __init__(self, number): self.number = number def __gt__(self, obj): print("__gt__ has called.") return self.number > obj.number def __ge__(self, obj): print("__ge__ has called.") # 既然执行了这个函数,你可以尽情的在return之前写想写的功能 return self.number >= obj.number a = A(23) # 当你写 23 > 45时,实际等同于 a = int(23), b = int(45); a > b b = A(45) # 这里A就相当于int c = A(45) print(a > b) print(b >= c) """ __gt__ has called. False __ge__ has called. True """
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
1 | 加法 | x + y |
x.__add__(y) |
2 | 减法 | x - y |
x.__sub__(y) |
3 | 乘法 | x * y |
x.__mul__(y) |
4 | 除法 | x / y |
x.__truediv__(y) |
5 | 地板除 | x // y |
x.__floordiv__(y) |
6 | 取模(取余) | x % y |
x.__mod__(y) |
7 | 地板除 & 取模 | divmod(x, y) |
x.__divmod__(y) |
8 | 乘幂 | x ** y |
x.__pow__(y) |
9 | 左位移 | x << y |
x.__lshift__(y) |
10 | 右位移 | x >> y |
x.__rshift__(y) |
11 | 按位 and |
x & y |
x.__and__(y) |
12 | 按位 xor |
x ^ y |
x.__xor__(y) |
13 | 按位 or |
x | y |
x.__or__(y) |
此外,还有原地操作:
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
1 | 原地加法 | x += y |
x.__iadd__(y) |
2 | 原地减法 | x -= y |
x.__isub__(y) |
3 | 原地乘法 | x *= y |
x.__imul__(y) |
4 | 原地除法 | x /= y |
x.__itruediv__(y) |
5 | 原地地板除法 | x //= y |
x.__ifloordiv__(y) |
6 | 原地取模 | x %= y |
x.__imod__(y) |
7 | 原地乘幂 | x **= y |
x.__ipow__(y) |
8 | 原地左位移 | x <<= y |
x.__ilshift__(y) |
9 | 原地右位移 | x >>= y |
x.__irshift__(y) |
10 | 原地按位 and |
x &= y |
x.__iand__(y) |
11 | 原地按位 xor |
x ^= y |
x.__ixor__(y) |
12 | 原地按位 or |
x |= y |
x.__ior__(y) |
以及一些自身简单运算:
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
1 | 负数 | -x |
x.__neg__() |
2 | 正数 | +x |
x.__pos__() |
3 | 绝对值 | abs(x) |
x.__abs__() |
4 | 取反 | ~x |
x.__invert__() |
5 | 复数 | complex(x) |
x.__complex__() |
6 | 整数转换 | int(x) |
x.__int__() |
7 | 浮点数 | float(x) |
x.__float__() |
8 | 四舍五入至最近的整数 | round(x) |
x.__round__() |
9 | 四舍五入至最近的 n 位小数 | round(x, n) |
x.__round__(n) |
10 | >= x 的最小整数 |
math.ceil(x) |
x.__ceil__() |
11 | <= x 的最大整数 |
math.floor(x) |
x.__floor__() |
12 | 对 x 朝向 0 取整 |
math.trunc(x) |
x.__trunc__() |
13 | 作为列表索引的数字 | a_list[x] |
a_list[x.__index__()] |
五、跟字符串有关的
__str__、__repr__、__format__、[__bytes__]
这些都是跟字符串格式和字符串表示有关的特殊方法。
class Cls(object): def __init__(self): self.name = "alex" self.age = 27 self.gender = "female" def __str__(self): print("__str__ has called.") return "Str: my name is {}, {}.".format(self.name, self.age) def __format__(self, special): print("__format__ has called.") if special == "": return self.__str__() # 也可以写成str(self) for _, v in self.__dict__.items(): # 实际上是{"name": "alex", "age": 27} special = special.replace("%s", str(v), 1) return special
观察下面二段代码打印的结果:
cls = Cls() str1 = "{:%s, %s, %s}".format(cls) print("-------------") print(str1) """ __format__ has called. ------------- alex, 27, female """
cls = Cls() str2 = format(cls, "my name is %s, %s, %s.") print("-------------") print(str2) """ __format__ has called. ------------- my name is alex, 27, female. """
以上两端代码本质都是一样的,即format和一个字符串匹配,也就是__format__函数传递了special参数(字符串)。
当format函数没有和字符串匹配,也就是__format__函数没有传入参数special时,会调用__str__函数。
cls = Cls() str3 = format(cls) print("-------------") print(str3) """ __format__ has called. __str__ has called. ------------- Str: my name is alex, 27. """
str(cls)调用了__str__方法。str(cls)调用__str__方法, string.format(cls)和format(cls)调用__format__方法。
cls = Cls() print(str(cls)) """ __str__ has called. Str: my name is alex, 27. """
再来看__str__和__repr__的区别和联系。当__str__存在时,print(obj)和str(obj)实际调用了__str__方法;当__str__不存在时,就会调用__repr__。但是当定义了__repr__时,obj本身会返回一个"合法"的字符串表达式,而不再返回一个描述符<'__main__.A object at XXX>,当然这个描述符也可以被__str__修改,只是会被__repr__覆盖。
class A: def __str__(self): print("__str__ has called.") return self.__class__.__name__ + "__str__" a = A() a # <__main__.A at 0x110a764e0> print(a) # __str__ has called. # A__str__ str(a) # __str__ has called. # A__str__
class A: def __repr__(self): print("__repr__ has called.") return self.__class__.__name__ + "__repr__" a = A() a #__repr__ has called. #A__repr__ print(a) #__repr__ has called. #A__repr__ str(a) #__repr__ has called. #A__repr__
class A: def __str__(self): print("__str__ has called.") return self.__class__.__name__ + "__str__" def __repr__(self): print("__repr__ has called.") return self.__class__.__name__ + "__repr__" a = A() a #__repr__ has called. #A__repr__ print(a) #__str__ has called. #A__str__ str(a) #__str__ has called. #'A__str__'
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
1 | 初始化一个实例 | x = MyClass() |
x.__init__() |
2 | 字符串的“官方”表现形式 | repr(x) |
x.__repr__() |
3 | 字符串的“非正式”值 | str(x) |
x.__str__() |
4 | 字节数组的“非正式”值 | bytes(x) |
x.__bytes__() |
5 | 格式化字符串的值 | format(x, format_spec) |
x.__format__(format_spec) |
通常__str__()
和__repr__()
代码都是一样的,所以会这么写:
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name=%s)' % self.name __repr__ = __str__
六、跟迭代器有关的
序号 | 目的 | 所编写代码 | Python 实际调用 |
---|---|---|---|
1 | 遍历某个序列 | iter(seq) |
seq.__iter__() |
2 | 从迭代器中获取下一个值 | next(seq) |
seq.__next__() |
3 | 按逆序创建一个迭代器 | reversed(seq) |
seq.__reversed__() |
iter内置函数对应的特殊方法是__iter_,它把一个序列变成迭代器。
class Iter: def __init__(self, lis): self.iterate = lis def __iter__(self): for v in self.iterate: print("__iter__ has called.") yield v a = Iter(list(range(5))) b = iter(a) print(next(b)) print(next(b)) """ __iter__ has called. 0 __iter__ has called. 1 """
next内置函数对应的特殊方法是__iter_,对迭代器进行迭代(惰性行)。
class Iter: count = 0 def __init__(self, lis): self.iterate = lis def __next__(self): print("__next__ has called.") try: value = self.iterate[Iter.count] Iter.count += 1 except StopIteration: exit() return value a = Iter(list(range(5))) print(next(a)) print(next(a)) """ __next__ has called. 0 __next__ has called. 1 """
至此我们已了解了绝大部分特殊方法,在python中,类对象的顶层是type类。下面是object类和type类的源码。它们的源码是底层实现的。
class object: """ The most base type """ def __delattr__(self, *args, **kwargs): # real signature unknown """ Implement delattr(self, name). """ pass def __dir__(self): # real signature unknown; restored from __doc__ """ __dir__() -> list default dir() implementation """ return [] def __eq__(self, *args, **kwargs): # real signature unknown """ Return self==value. """ pass def __format__(self, *args, **kwargs): # real signature unknown """ default object formatter """ pass def __getattribute__(self, *args, **kwargs): # real signature unknown """ Return getattr(self, name). """ pass def __ge__(self, *args, **kwargs): # real signature unknown """ Return self>=value. """ pass def __gt__(self, *args, **kwargs): # real signature unknown """ Return self>value. """ pass def __hash__(self, *args, **kwargs): # real signature unknown """ Return hash(self). """ pass def __init_subclass__(self, *args, **kwargs): # real signature unknown """ This method is called when a class is subclassed. The default implementation does nothing. It may be overridden to extend subclasses. """ pass def __init__(self): # known special case of object.__init__ """ Initialize self. See help(type(self)) for accurate signature. """ pass def __le__(self, *args, **kwargs): # real signature unknown """ Return self<=value. """ pass def __lt__(self, *args, **kwargs): # real signature unknown """ Return self<value. """ pass @staticmethod # known case of __new__ def __new__(cls, *more): # known special case of object.__new__ """ Create and return a new object. See help(type) for accurate signature. """ pass def __ne__(self, *args, **kwargs): # real signature unknown """ Return self!=value. """ pass def __reduce_ex__(self, *args, **kwargs): # real signature unknown """ helper for pickle """ pass def __reduce__(self, *args, **kwargs): # real signature unknown """ helper for pickle """ pass def __repr__(self, *args, **kwargs): # real signature unknown """ Return repr(self). """ pass def __setattr__(self, *args, **kwargs): # real signature unknown """ Implement setattr(self, name, value). """ pass def __sizeof__(self): # real signature unknown; restored from __doc__ """ __sizeof__() -> int size of object in memory, in bytes """ return 0 def __str__(self, *args, **kwargs): # real signature unknown """ Return str(self). """ pass @classmethod # known case def __subclasshook__(cls, subclass): # known special case of object.__subclasshook__ """ Abstract classes can override this to customize issubclass(). This is invoked early on by abc.ABCMeta.__subclasscheck__(). It should return True, False or NotImplemented. If it returns NotImplemented, the normal algorithm is used. Otherwise, it overrides the normal algorithm (and the outcome is cached). """ pass __class__ = None # (!) forward: type, real value is '' __dict__ = {} __doc__ = '' __module__ = ''
class type(object): """ type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type """ def mro(self): # real signature unknown; restored from __doc__ """ mro() -> list return a type's method resolution order """ return [] def __call__(self, *args, **kwargs): # real signature unknown """ Call self as a function. """ pass def __delattr__(self, *args, **kwargs): # real signature unknown """ Implement delattr(self, name). """ pass def __dir__(self): # real signature unknown; restored from __doc__ """ __dir__() -> list specialized __dir__ implementation for types """ return [] def __getattribute__(self, *args, **kwargs): # real signature unknown """ Return getattr(self, name). """ pass def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__ """ type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type # (copied from class doc) """ pass def __instancecheck__(self): # real signature unknown; restored from __doc__ """ __instancecheck__() -> bool check if an object is an instance """ return False @staticmethod # known case of __new__ def __new__(*args, **kwargs): # real signature unknown """ Create and return a new object. See help(type) for accurate signature. """ pass def __prepare__(self): # real signature unknown; restored from __doc__ """ __prepare__() -> dict used to create the namespace for the class statement """ return {} def __repr__(self, *args, **kwargs): # real signature unknown """ Return repr(self). """ pass def __setattr__(self, *args, **kwargs): # real signature unknown """ Implement setattr(self, name, value). """ pass def __sizeof__(self): # real signature unknown; restored from __doc__ """ __sizeof__() -> int return memory consumption of the type object """ return 0 def __subclasscheck__(self): # real signature unknown; restored from __doc__ """ __subclasscheck__() -> bool check if a class is a subclass """ return False def __subclasses__(self): # real signature unknown; restored from __doc__ """ __subclasses__() -> list of immediate subclasses """ return [] __abstractmethods__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default __bases__ = ( object, ) __base__ = object __basicsize__ = 864 __dictoffset__ = 264 __dict__ = None # (!) real value is '' __flags__ = 2148291584 __itemsize__ = 40 __mro__ = ( None, # (!) forward: type, real value is '' object, ) __name__ = 'type' __qualname__ = 'type' __text_signature__ = None __weakrefoffset__ = 368