这篇文章介绍了魔法方法,并带你使用魔法方法实现自定义的序列。
魔法方法
魔法方法是为了增强某个类的特性,它有约定俗成的名字,你只需要实现这个方法即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Company(object): def __init__(self, employee_list): self.employee = employee_list
def __getitem__(self, item): return self.employee[item]
company = Company(["Apple", "MicroSoft"])
for company_name in company: print(company_name)
company[:2]
|
有哪些魔法方法
首先可以分为非数学运算与数学相关。
非数学运算中又有:
1 2 3 4 5 6 7 8 9 10
| - 字符串表示,__repr__, __str__ - 集合序列相关,__len__, __get/set/delitem__, __contains__ - 迭代相关, __iter__, __next__ - 可调用, __call__ - with 上下文管理器, __enter__, __exit__ - 数值转换,__abs__, __int/float/bool/...__, __hash__ - 元类相关, __new__, __init__ - 属性相关,__get/setattr__, __get/setattribute__. __dir__ - 属性描述符, __get__, __set__, __delete__ - 协程, __await__, __aiter__, __anext__, __aenter__, __aexit__
|
数学运算则有一元、二元运算符,算数运算符,位运算符等等,暂时不做过多介绍。
在 Python 中,len 方法有其特殊性。当 len 作用在内置类型如 set, list, dict 上的时候,因为这些结构都是用 C 语言实现的,性能非常高,当 len 计算这些数据结构的长度的时候,会直接读取这个数据结构的长度值(C 会维护一个长度值),而不会遍历该树结构。
应用:自定义序列
序列类型
在了解如何自定义序列类之前,我们先看 python 有哪些内置的序列类,我们将其中分为这几类:
1 2 3 4 5 6 7 8
| - 容器序列:list, tuple, deque
- 扁平序列:str, bytes, bytearray, array.array
- 可变序列: list, deque, bytearray, array
- 不可变:str, tuple, bytes
|
上面这些序列类,你都可以通过 for 循环去访问其内部的元素。
要想实现序列类,则需要实现序列的协议。
可以通过 _collections_abc
了解要实现序列协议所需要的函数。
在序列中,一般都支持 + += extend
方法,但你知道他们的区别吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| l = [1, 2]
c = a + [3, 4]
a += [3, 4]
a += (3, 4)
c = a + (1, 2)
|
+=
支持任意序列类型,其背后原理是调用一个魔法函数 __iadd__
, 其中又是依赖 __extend__
方法,内部使用的是 for 循环对元素取值并相加,所以只要是可以迭代的类型,都支持用 +=
操作。
另外要注意 list 的 append
和 extend
方法的区别,extend 是将数组内的值一个一个放入另一个数组,而 append 是将整个数组放入另一个数组。
1 2 3 4 5 6
| arr = [1, 2]
arr.extend([3, 4])
arr.append([3, 4]) arr.append((3, 4))
|
实现可切片对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| import numbers
class DCGroup: def __init__(self, dpt, industry, staff): self.dpt = dpt self.industry = industry self.staff = staff
def __reversed__(self): self.staff.reverse()
def __getitem__(self, item):
cls = type(self) if isinstance(item, slice): return cls( dpt=self.dpt, industry=self.industry, staff=self.staff[item] ) elif isinstance(item, numbers.Integral): return cls( dpt=self.dpt, industry=self.industry, staff=[self.staff[item]] )
def __len__(self): return len(self.staff)
def __iter__(self): return iter(self.staff)
def __contains__(self, item): if item in self.staff: return True else: return False
dc = DCGroup(dpt='DC', industry='IMF', staff=['Scott', 'Austin'])
dc[:1].staff 'Scott' in dc len(dc)
for user in dc: print(user)
reversed(dc)
|
拓展:维护已排序序列
bisect
是用来处理已排序的升序序列的包,使用的是二分查找。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import bisect
int_list = [] bisect.insort(int_list, 3) bisect.insort(int_list, 2) bisect.insort(int_list, 1) bisect.insort(int_list, 5) bisect.insort(int_list, 9)
print(int_list)
print(bisect.bisect(int_list, 3))
print(bisect.bisect_left(int_list, 3)) print(bisect.bisect_right(int_list, 3))
|
Python 中还有其他的数据结构,比如 array, deque.
list 相当于容器,可以存放任意类型,而array 只能存放指定数据类型。
1 2
| my_array = array.array("i")
|