This recipe creates a collection proxy which allows several lists to be treated as one. When the collection proxy is accessed/modified, it is as though the operation was performed one the underlying lists.
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | from collections import namedtuple, OrderedDict
from lib import *
def virtual_container(virtual_container, objects_type):
"""Used to create a virtual object given a the type of container and what it holds.
The object_type needs to only have normal values."""
for name in objects_type:
if name.startswith("__") and name.endswith("__"):
raise ValueError("Arguments should be not start and end with __")
if issubclass(virtual_container,list):
def handle_binding(objects_type):
class my_virtual_container_class:
"""This singleton class represents the container"""
def __init__(self):
"""The attributes for the virtual container"""
#Define the default values
__vals__=OrderedDict([(key,None) for key in objects_type])
#Then function to access them. Note the default parameter for the lambda to
#enforce early binding
d={}
for key in objects_type:
def func_factory(key):
"""Function causes early binding for key"""
def make_attribute(self, items=None):
"""Called on the attr_cl. This function will return the list for the attribute.
As a default, this is [], but a list can also be passed in"""
if items==None:
items=[]
if self.__vals__[key]==None:
self.__vals__[key]=items
else:
raise RuntimeError("This attribute has been already initialised")
return self.__vals__[key]
return make_attribute
#Add the function to this class
d[key]=func_factory(key)
d["__vals__"]=__vals__
#Construct a named tuple from this
self.__attr__=type('attr_cl',(), d)()
#Define the operators
def __iter__(self):
vals=self.__attr__.__vals__
return zip(*vals.values())
def __contains__(self, x):
return x in self.__iter__()
def __delitem__(self, i):
#Handles items and slices
vals=self.__attr__.__vals__
for seq in vals.values():
del seq[i]
def __getitem__(self, i):
#Handles items and slices
vals=self.__attr__.__vals__
#Make it a slice
isslice=isinstance(i, slice)
if not isslice:
i=slice(i, i+1)
ret=[seq[i] for seq in vals.values()]
ret=list(zip(*ret))
if not isslice:
ret=ret[0]
return ret
def __setitem__(self, i, x):
#Handles items and slices
vals=self.__attr__.__vals__
#Split slices
if isinstance(i,slice):
x=zip(*x)
for seq, setTo in zip(vals.values(), x):
seq[i]=setTo
def __add__(self, myList):
return list(self.__iter__())+myList
def __iadd__(self, myList):
vals=self.__attr__.__vals__
for seq, extra in zip(vals.values(), zip(*myList)):
seq+=extra
return self
def __str__(self):
s="Proxy: Names: "+" ".join(self.__attr__.__vals__.keys())
s+=" Vals: "+" ".join(map(str,self.__iter__()))
return s
return my_virtual_container_class()
return handle_binding(objects_type)
#Example code
coordinates_2d=["x","y"]
coordinates=virtual_container(list, coordinates_2d)
x=coordinates.__attr__.x([1,2,3,4])
y=coordinates.__attr__.y([2,3,4,5])
print(coordinates[3])
print(coordinates[3:4])
print(coordinates[2:4])
for m in coordinates:
print(m, end=" ")
print()
print(coordinates+[1,2])
coordinates[1]=(0,0)
print(x,y)
coordinates+=[(9,9)]
print("##",coordinates[:])
del coordinates[1:3]
print(coordinates[:])
coordinates[0:2]=[(0,1),(3,4)]
print(coordinates)
#Testing second group of coordinates
coordinates2=virtual_container(list, coordinates_2d)
x2=coordinates2.__attr__.x([])
y2=coordinates2.__attr__.y([])
print("List is: ", end=" ")
for z in coordinates2:
print(z,end=" ")
print()
print(bool(coordinates2))
print(coordinates[0])
|
Lazy operations were used when possible. While efforts were made to keep this efficient, since list access is a quick operation, the overhead as a proportion with probably be significant. I would like to consider mocking up other structures as well.
This is a great idea. It is reminiscent of recipe 305268 for dictionary chains.