Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pyrsistent/_pset.py: 59%

92 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 06:54 +0000

1from collections.abc import Set, Hashable 

2import sys 

3from pyrsistent._pmap import pmap 

4 

5 

6class PSet(object): 

7 """ 

8 Persistent set implementation. Built on top of the persistent map. The set supports all operations 

9 in the Set protocol and is Hashable. 

10 

11 Do not instantiate directly, instead use the factory functions :py:func:`s` or :py:func:`pset` 

12 to create an instance. 

13 

14 Random access and insert is log32(n) where n is the size of the set. 

15 

16 Some examples: 

17 

18 >>> s = pset([1, 2, 3, 1]) 

19 >>> s2 = s.add(4) 

20 >>> s3 = s2.remove(2) 

21 >>> s 

22 pset([1, 2, 3]) 

23 >>> s2 

24 pset([1, 2, 3, 4]) 

25 >>> s3 

26 pset([1, 3, 4]) 

27 """ 

28 __slots__ = ('_map', '__weakref__') 

29 

30 def __new__(cls, m): 

31 self = super(PSet, cls).__new__(cls) 

32 self._map = m 

33 return self 

34 

35 def __contains__(self, element): 

36 return element in self._map 

37 

38 def __iter__(self): 

39 return iter(self._map) 

40 

41 def __len__(self): 

42 return len(self._map) 

43 

44 def __repr__(self): 

45 if not self: 

46 return 'p' + str(set(self)) 

47 

48 return 'pset([{0}])'.format(str(set(self))[1:-1]) 

49 

50 def __str__(self): 

51 return self.__repr__() 

52 

53 def __hash__(self): 

54 return hash(self._map) 

55 

56 def __reduce__(self): 

57 # Pickling support 

58 return pset, (list(self),) 

59 

60 @classmethod 

61 def _from_iterable(cls, it, pre_size=8): 

62 return PSet(pmap(dict((k, True) for k in it), pre_size=pre_size)) 

63 

64 def add(self, element): 

65 """ 

66 Return a new PSet with element added 

67 

68 >>> s1 = s(1, 2) 

69 >>> s1.add(3) 

70 pset([1, 2, 3]) 

71 """ 

72 return self.evolver().add(element).persistent() 

73 

74 def update(self, iterable): 

75 """ 

76 Return a new PSet with elements in iterable added 

77 

78 >>> s1 = s(1, 2) 

79 >>> s1.update([3, 4, 4]) 

80 pset([1, 2, 3, 4]) 

81 """ 

82 e = self.evolver() 

83 for element in iterable: 

84 e.add(element) 

85 

86 return e.persistent() 

87 

88 def remove(self, element): 

89 """ 

90 Return a new PSet with element removed. Raises KeyError if element is not present. 

91 

92 >>> s1 = s(1, 2) 

93 >>> s1.remove(2) 

94 pset([1]) 

95 """ 

96 if element in self._map: 

97 return self.evolver().remove(element).persistent() 

98 

99 raise KeyError("Element '%s' not present in PSet" % repr(element)) 

100 

101 def discard(self, element): 

102 """ 

103 Return a new PSet with element removed. Returns itself if element is not present. 

104 """ 

105 if element in self._map: 

106 return self.evolver().remove(element).persistent() 

107 

108 return self 

109 

110 class _Evolver(object): 

111 __slots__ = ('_original_pset', '_pmap_evolver') 

112 

113 def __init__(self, original_pset): 

114 self._original_pset = original_pset 

115 self._pmap_evolver = original_pset._map.evolver() 

116 

117 def add(self, element): 

118 self._pmap_evolver[element] = True 

119 return self 

120 

121 def remove(self, element): 

122 del self._pmap_evolver[element] 

123 return self 

124 

125 def is_dirty(self): 

126 return self._pmap_evolver.is_dirty() 

127 

128 def persistent(self): 

129 if not self.is_dirty(): 

130 return self._original_pset 

131 

132 return PSet(self._pmap_evolver.persistent()) 

133 

134 def __len__(self): 

135 return len(self._pmap_evolver) 

136 

137 def copy(self): 

138 return self 

139 

140 def evolver(self): 

141 """ 

142 Create a new evolver for this pset. For a discussion on evolvers in general see the 

143 documentation for the pvector evolver. 

144 

145 Create the evolver and perform various mutating updates to it: 

146 

147 >>> s1 = s(1, 2, 3) 

148 >>> e = s1.evolver() 

149 >>> _ = e.add(4) 

150 >>> len(e) 

151 4 

152 >>> _ = e.remove(1) 

153 

154 The underlying pset remains the same: 

155 

156 >>> s1 

157 pset([1, 2, 3]) 

158 

159 The changes are kept in the evolver. An updated pmap can be created using the 

160 persistent() function on the evolver. 

161 

162 >>> s2 = e.persistent() 

163 >>> s2 

164 pset([2, 3, 4]) 

165 

166 The new pset will share data with the original pset in the same way that would have 

167 been done if only using operations on the pset. 

168 """ 

169 return PSet._Evolver(self) 

170 

171 # All the operations and comparisons you would expect on a set. 

172 # 

173 # This is not very beautiful. If we avoid inheriting from PSet we can use the 

174 # __slots__ concepts (which requires a new style class) and hopefully save some memory. 

175 __le__ = Set.__le__ 

176 __lt__ = Set.__lt__ 

177 __gt__ = Set.__gt__ 

178 __ge__ = Set.__ge__ 

179 __eq__ = Set.__eq__ 

180 __ne__ = Set.__ne__ 

181 

182 __and__ = Set.__and__ 

183 __or__ = Set.__or__ 

184 __sub__ = Set.__sub__ 

185 __xor__ = Set.__xor__ 

186 

187 issubset = __le__ 

188 issuperset = __ge__ 

189 union = __or__ 

190 intersection = __and__ 

191 difference = __sub__ 

192 symmetric_difference = __xor__ 

193 

194 isdisjoint = Set.isdisjoint 

195 

196Set.register(PSet) 

197Hashable.register(PSet) 

198 

199_EMPTY_PSET = PSet(pmap()) 

200 

201 

202def pset(iterable=(), pre_size=8): 

203 """ 

204 Creates a persistent set from iterable. Optionally takes a sizing parameter equivalent to that 

205 used for :py:func:`pmap`. 

206 

207 >>> s1 = pset([1, 2, 3, 2]) 

208 >>> s1 

209 pset([1, 2, 3]) 

210 """ 

211 if not iterable: 

212 return _EMPTY_PSET 

213 

214 return PSet._from_iterable(iterable, pre_size=pre_size) 

215 

216 

217def s(*elements): 

218 """ 

219 Create a persistent set. 

220 

221 Takes an arbitrary number of arguments to insert into the new set. 

222 

223 >>> s1 = s(1, 2, 3, 2) 

224 >>> s1 

225 pset([1, 2, 3]) 

226 """ 

227 return pset(elements)