##// END OF EJS Templates
Struct no pprints the dict inside it
vivainio -
Show More
@@ -1,393 +1,394 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Mimic C structs with lots of extra functionality.
2 """Mimic C structs with lots of extra functionality.
3
3
4 $Id: ipstruct.py 1949 2006-11-28 19:12:15Z vivainio $"""
4 $Id: ipstruct.py 1950 2006-11-28 19:15:35Z vivainio $"""
5
5
6 #*****************************************************************************
6 #*****************************************************************************
7 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
7 # Copyright (C) 2001-2004 Fernando Perez <fperez@colorado.edu>
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #*****************************************************************************
11 #*****************************************************************************
12
12
13 from IPython import Release
13 from IPython import Release
14 __author__ = '%s <%s>' % Release.authors['Fernando']
14 __author__ = '%s <%s>' % Release.authors['Fernando']
15 __license__ = Release.license
15 __license__ = Release.license
16
16
17 __all__ = ['Struct']
17 __all__ = ['Struct']
18
18
19 import types
19 import types
20 import pprint
20
21
21 from IPython.genutils import list2dict2
22 from IPython.genutils import list2dict2
22
23
23 class Struct:
24 class Struct:
24 """Class to mimic C structs but also provide convenient dictionary-like
25 """Class to mimic C structs but also provide convenient dictionary-like
25 functionality.
26 functionality.
26
27
27 Instances can be initialized with a dictionary, a list of key=value pairs
28 Instances can be initialized with a dictionary, a list of key=value pairs
28 or both. If both are present, the dictionary must come first.
29 or both. If both are present, the dictionary must come first.
29
30
30 Because Python classes provide direct assignment to their members, it's
31 Because Python classes provide direct assignment to their members, it's
31 easy to overwrite normal methods (S.copy = 1 would destroy access to
32 easy to overwrite normal methods (S.copy = 1 would destroy access to
32 S.copy()). For this reason, all builtin method names are protected and
33 S.copy()). For this reason, all builtin method names are protected and
33 can't be assigned to. An attempt to do s.copy=1 or s['copy']=1 will raise
34 can't be assigned to. An attempt to do s.copy=1 or s['copy']=1 will raise
34 a KeyError exception. If you really want to, you can bypass this
35 a KeyError exception. If you really want to, you can bypass this
35 protection by directly assigning to __dict__: s.__dict__['copy']=1 will
36 protection by directly assigning to __dict__: s.__dict__['copy']=1 will
36 still work. Doing this will break functionality, though. As in most of
37 still work. Doing this will break functionality, though. As in most of
37 Python, namespace protection is weakly enforced, so feel free to shoot
38 Python, namespace protection is weakly enforced, so feel free to shoot
38 yourself if you really want to.
39 yourself if you really want to.
39
40
40 Note that this class uses more memory and is *much* slower than a regular
41 Note that this class uses more memory and is *much* slower than a regular
41 dictionary, so be careful in situations where memory or performance are
42 dictionary, so be careful in situations where memory or performance are
42 critical. But for day to day use it should behave fine. It is particularly
43 critical. But for day to day use it should behave fine. It is particularly
43 convenient for storing configuration data in programs.
44 convenient for storing configuration data in programs.
44
45
45 +,+=,- and -= are implemented. +/+= do merges (non-destructive updates),
46 +,+=,- and -= are implemented. +/+= do merges (non-destructive updates),
46 -/-= remove keys from the original. See the method descripitions.
47 -/-= remove keys from the original. See the method descripitions.
47
48
48 This class allows a quick access syntax: both s.key and s['key'] are
49 This class allows a quick access syntax: both s.key and s['key'] are
49 valid. This syntax has a limitation: each 'key' has to be explicitly
50 valid. This syntax has a limitation: each 'key' has to be explicitly
50 accessed by its original name. The normal s.key syntax doesn't provide
51 accessed by its original name. The normal s.key syntax doesn't provide
51 access to the keys via variables whose values evaluate to the desired
52 access to the keys via variables whose values evaluate to the desired
52 keys. An example should clarify this:
53 keys. An example should clarify this:
53
54
54 Define a dictionary and initialize both with dict and k=v pairs:
55 Define a dictionary and initialize both with dict and k=v pairs:
55 >>> d={'a':1,'b':2}
56 >>> d={'a':1,'b':2}
56 >>> s=Struct(d,hi=10,ho=20)
57 >>> s=Struct(d,hi=10,ho=20)
57 The return of __repr__ can be used to create a new instance:
58 The return of __repr__ can be used to create a new instance:
58 >>> s
59 >>> s
59 Struct({'ho': 20, 'b': 2, 'hi': 10, 'a': 1})
60 Struct({'ho': 20, 'b': 2, 'hi': 10, 'a': 1})
60 __str__ (called by print) shows it's not quite a regular dictionary:
61 __str__ (called by print) shows it's not quite a regular dictionary:
61 >>> print s
62 >>> print s
62 Struct {a: 1, b: 2, hi: 10, ho: 20}
63 Struct {a: 1, b: 2, hi: 10, ho: 20}
63 Access by explicitly named key with dot notation:
64 Access by explicitly named key with dot notation:
64 >>> s.a
65 >>> s.a
65 1
66 1
66 Or like a dictionary:
67 Or like a dictionary:
67 >>> s['a']
68 >>> s['a']
68 1
69 1
69 If you want a variable to hold the key value, only dictionary access works:
70 If you want a variable to hold the key value, only dictionary access works:
70 >>> key='hi'
71 >>> key='hi'
71 >>> s.key
72 >>> s.key
72 Traceback (most recent call last):
73 Traceback (most recent call last):
73 File "<stdin>", line 1, in ?
74 File "<stdin>", line 1, in ?
74 AttributeError: Struct instance has no attribute 'key'
75 AttributeError: Struct instance has no attribute 'key'
75 >>> s[key]
76 >>> s[key]
76 10
77 10
77
78
78 Another limitation of the s.key syntax (and Struct(key=val)
79 Another limitation of the s.key syntax (and Struct(key=val)
79 initialization): keys can't be numbers. But numeric keys can be used and
80 initialization): keys can't be numbers. But numeric keys can be used and
80 accessed using the dictionary syntax. Again, an example:
81 accessed using the dictionary syntax. Again, an example:
81
82
82 This doesn't work:
83 This doesn't work:
83 >>> s=Struct(4='hi')
84 >>> s=Struct(4='hi')
84 SyntaxError: keyword can't be an expression
85 SyntaxError: keyword can't be an expression
85 But this does:
86 But this does:
86 >>> s=Struct()
87 >>> s=Struct()
87 >>> s[4]='hi'
88 >>> s[4]='hi'
88 >>> s
89 >>> s
89 Struct({4: 'hi'})
90 Struct({4: 'hi'})
90 >>> s[4]
91 >>> s[4]
91 'hi'
92 'hi'
92 """
93 """
93
94
94 # Attributes to which __setitem__ and __setattr__ will block access.
95 # Attributes to which __setitem__ and __setattr__ will block access.
95 # Note: much of this will be moot in Python 2.2 and will be done in a much
96 # Note: much of this will be moot in Python 2.2 and will be done in a much
96 # cleaner way.
97 # cleaner way.
97 __protected = ('copy dict dictcopy get has_attr has_key items keys '
98 __protected = ('copy dict dictcopy get has_attr has_key items keys '
98 'merge popitem setdefault update values '
99 'merge popitem setdefault update values '
99 '__make_dict __dict_invert ').split()
100 '__make_dict __dict_invert ').split()
100
101
101 def __init__(self,dict=None,**kw):
102 def __init__(self,dict=None,**kw):
102 """Initialize with a dictionary, another Struct, or by giving
103 """Initialize with a dictionary, another Struct, or by giving
103 explicitly the list of attributes.
104 explicitly the list of attributes.
104
105
105 Both can be used, but the dictionary must come first:
106 Both can be used, but the dictionary must come first:
106 Struct(dict), Struct(k1=v1,k2=v2) or Struct(dict,k1=v1,k2=v2).
107 Struct(dict), Struct(k1=v1,k2=v2) or Struct(dict,k1=v1,k2=v2).
107 """
108 """
108 self.__dict__['__allownew'] = True
109 self.__dict__['__allownew'] = True
109 if dict is None:
110 if dict is None:
110 dict = {}
111 dict = {}
111 if isinstance(dict,Struct):
112 if isinstance(dict,Struct):
112 dict = dict.dict()
113 dict = dict.dict()
113 elif dict and type(dict) is not types.DictType:
114 elif dict and type(dict) is not types.DictType:
114 raise TypeError,\
115 raise TypeError,\
115 'Initialize with a dictionary or key=val pairs.'
116 'Initialize with a dictionary or key=val pairs.'
116 dict.update(kw)
117 dict.update(kw)
117 # do the updating by hand to guarantee that we go through the
118 # do the updating by hand to guarantee that we go through the
118 # safety-checked __setitem__
119 # safety-checked __setitem__
119 for k,v in dict.items():
120 for k,v in dict.items():
120 self[k] = v
121 self[k] = v
121
122
122
123
123 def __setitem__(self,key,value):
124 def __setitem__(self,key,value):
124 """Used when struct[key] = val calls are made."""
125 """Used when struct[key] = val calls are made."""
125 if key in Struct.__protected:
126 if key in Struct.__protected:
126 raise KeyError,'Key '+`key`+' is a protected key of class Struct.'
127 raise KeyError,'Key '+`key`+' is a protected key of class Struct.'
127 if not self['__allownew'] and key not in self.__dict__:
128 if not self['__allownew'] and key not in self.__dict__:
128 raise KeyError(
129 raise KeyError(
129 "Can't create unknown attribute %s - Check for typos, or use allow_new_attr to create new attributes!" %
130 "Can't create unknown attribute %s - Check for typos, or use allow_new_attr to create new attributes!" %
130 key)
131 key)
131
132
132 self.__dict__[key] = value
133 self.__dict__[key] = value
133
134
134 def __setattr__(self, key, value):
135 def __setattr__(self, key, value):
135 """Used when struct.key = val calls are made."""
136 """Used when struct.key = val calls are made."""
136 self.__setitem__(key,value)
137 self.__setitem__(key,value)
137
138
138 def __str__(self):
139 def __str__(self):
139 """Gets called by print."""
140 """Gets called by print."""
140
141
141 return 'Struct('+str(self.__dict__)+')'
142 return 'Struct('+ pprint.pformat(self.__dict__)+')'
142
143
143 def __repr__(self):
144 def __repr__(self):
144 """Gets called by repr.
145 """Gets called by repr.
145
146
146 A Struct can be recreated with S_new=eval(repr(S_old))."""
147 A Struct can be recreated with S_new=eval(repr(S_old))."""
147 return 'Struct('+str(self.__dict__)+')'
148 return self.__str__()
148
149
149 def __getitem__(self,key):
150 def __getitem__(self,key):
150 """Allows struct[key] access."""
151 """Allows struct[key] access."""
151 return self.__dict__[key]
152 return self.__dict__[key]
152
153
153 def __contains__(self,key):
154 def __contains__(self,key):
154 """Allows use of the 'in' operator."""
155 """Allows use of the 'in' operator."""
155 return self.__dict__.has_key(key)
156 return self.__dict__.has_key(key)
156
157
157 def __iadd__(self,other):
158 def __iadd__(self,other):
158 """S += S2 is a shorthand for S.merge(S2)."""
159 """S += S2 is a shorthand for S.merge(S2)."""
159 self.merge(other)
160 self.merge(other)
160 return self
161 return self
161
162
162 def __add__(self,other):
163 def __add__(self,other):
163 """S + S2 -> New Struct made form S and S.merge(S2)"""
164 """S + S2 -> New Struct made form S and S.merge(S2)"""
164 Sout = self.copy()
165 Sout = self.copy()
165 Sout.merge(other)
166 Sout.merge(other)
166 return Sout
167 return Sout
167
168
168 def __sub__(self,other):
169 def __sub__(self,other):
169 """Return S1-S2, where all keys in S2 have been deleted (if present)
170 """Return S1-S2, where all keys in S2 have been deleted (if present)
170 from S1."""
171 from S1."""
171 Sout = self.copy()
172 Sout = self.copy()
172 Sout -= other
173 Sout -= other
173 return Sout
174 return Sout
174
175
175 def __isub__(self,other):
176 def __isub__(self,other):
176 """Do in place S = S - S2, meaning all keys in S2 have been deleted
177 """Do in place S = S - S2, meaning all keys in S2 have been deleted
177 (if present) from S1."""
178 (if present) from S1."""
178
179
179 for k in other.keys():
180 for k in other.keys():
180 if self.has_key(k):
181 if self.has_key(k):
181 del self.__dict__[k]
182 del self.__dict__[k]
182
183
183 def __make_dict(self,__loc_data__,**kw):
184 def __make_dict(self,__loc_data__,**kw):
184 "Helper function for update and merge. Return a dict from data."
185 "Helper function for update and merge. Return a dict from data."
185
186
186 if __loc_data__ == None:
187 if __loc_data__ == None:
187 dict = {}
188 dict = {}
188 elif type(__loc_data__) is types.DictType:
189 elif type(__loc_data__) is types.DictType:
189 dict = __loc_data__
190 dict = __loc_data__
190 elif isinstance(__loc_data__,Struct):
191 elif isinstance(__loc_data__,Struct):
191 dict = __loc_data__.__dict__
192 dict = __loc_data__.__dict__
192 else:
193 else:
193 raise TypeError, 'Update with a dict, a Struct or key=val pairs.'
194 raise TypeError, 'Update with a dict, a Struct or key=val pairs.'
194 if kw:
195 if kw:
195 dict.update(kw)
196 dict.update(kw)
196 return dict
197 return dict
197
198
198 def __dict_invert(self,dict):
199 def __dict_invert(self,dict):
199 """Helper function for merge. Takes a dictionary whose values are
200 """Helper function for merge. Takes a dictionary whose values are
200 lists and returns a dict. with the elements of each list as keys and
201 lists and returns a dict. with the elements of each list as keys and
201 the original keys as values."""
202 the original keys as values."""
202
203
203 outdict = {}
204 outdict = {}
204 for k,lst in dict.items():
205 for k,lst in dict.items():
205 if type(lst) is types.StringType:
206 if type(lst) is types.StringType:
206 lst = lst.split()
207 lst = lst.split()
207 for entry in lst:
208 for entry in lst:
208 outdict[entry] = k
209 outdict[entry] = k
209 return outdict
210 return outdict
210
211
211 def clear(self):
212 def clear(self):
212 """Clear all attributes."""
213 """Clear all attributes."""
213 self.__dict__.clear()
214 self.__dict__.clear()
214
215
215 def copy(self):
216 def copy(self):
216 """Return a (shallow) copy of a Struct."""
217 """Return a (shallow) copy of a Struct."""
217 return Struct(self.__dict__.copy())
218 return Struct(self.__dict__.copy())
218
219
219 def dict(self):
220 def dict(self):
220 """Return the Struct's dictionary."""
221 """Return the Struct's dictionary."""
221 return self.__dict__
222 return self.__dict__
222
223
223 def dictcopy(self):
224 def dictcopy(self):
224 """Return a (shallow) copy of the Struct's dictionary."""
225 """Return a (shallow) copy of the Struct's dictionary."""
225 return self.__dict__.copy()
226 return self.__dict__.copy()
226
227
227 def popitem(self):
228 def popitem(self):
228 """S.popitem() -> (k, v), remove and return some (key, value) pair as
229 """S.popitem() -> (k, v), remove and return some (key, value) pair as
229 a 2-tuple; but raise KeyError if S is empty."""
230 a 2-tuple; but raise KeyError if S is empty."""
230 return self.__dict__.popitem()
231 return self.__dict__.popitem()
231
232
232 def update(self,__loc_data__=None,**kw):
233 def update(self,__loc_data__=None,**kw):
233 """Update (merge) with data from another Struct or from a dictionary.
234 """Update (merge) with data from another Struct or from a dictionary.
234 Optionally, one or more key=value pairs can be given at the end for
235 Optionally, one or more key=value pairs can be given at the end for
235 direct update."""
236 direct update."""
236
237
237 # The funny name __loc_data__ is to prevent a common variable name which
238 # The funny name __loc_data__ is to prevent a common variable name which
238 # could be a fieled of a Struct to collide with this parameter. The problem
239 # could be a fieled of a Struct to collide with this parameter. The problem
239 # would arise if the function is called with a keyword with this same name
240 # would arise if the function is called with a keyword with this same name
240 # that a user means to add as a Struct field.
241 # that a user means to add as a Struct field.
241 newdict = Struct.__make_dict(self,__loc_data__,**kw)
242 newdict = Struct.__make_dict(self,__loc_data__,**kw)
242 for k,v in newdict.items():
243 for k,v in newdict.items():
243 self[k] = v
244 self[k] = v
244
245
245 def merge(self,__loc_data__=None,__conflict_solve=None,**kw):
246 def merge(self,__loc_data__=None,__conflict_solve=None,**kw):
246 """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S.
247 """S.merge(data,conflict,k=v1,k=v2,...) -> merge data and k=v into S.
247
248
248 This is similar to update(), but much more flexible. First, a dict is
249 This is similar to update(), but much more flexible. First, a dict is
249 made from data+key=value pairs. When merging this dict with the Struct
250 made from data+key=value pairs. When merging this dict with the Struct
250 S, the optional dictionary 'conflict' is used to decide what to do.
251 S, the optional dictionary 'conflict' is used to decide what to do.
251
252
252 If conflict is not given, the default behavior is to preserve any keys
253 If conflict is not given, the default behavior is to preserve any keys
253 with their current value (the opposite of the update method's
254 with their current value (the opposite of the update method's
254 behavior).
255 behavior).
255
256
256 conflict is a dictionary of binary functions which will be used to
257 conflict is a dictionary of binary functions which will be used to
257 solve key conflicts. It must have the following structure:
258 solve key conflicts. It must have the following structure:
258
259
259 conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc }
260 conflict == { fn1 : [Skey1,Skey2,...], fn2 : [Skey3], etc }
260
261
261 Values must be lists or whitespace separated strings which are
262 Values must be lists or whitespace separated strings which are
262 automatically converted to lists of strings by calling string.split().
263 automatically converted to lists of strings by calling string.split().
263
264
264 Each key of conflict is a function which defines a policy for
265 Each key of conflict is a function which defines a policy for
265 resolving conflicts when merging with the input data. Each fn must be
266 resolving conflicts when merging with the input data. Each fn must be
266 a binary function which returns the desired outcome for a key
267 a binary function which returns the desired outcome for a key
267 conflict. These functions will be called as fn(old,new).
268 conflict. These functions will be called as fn(old,new).
268
269
269 An example is probably in order. Suppose you are merging the struct S
270 An example is probably in order. Suppose you are merging the struct S
270 with a dict D and the following conflict policy dict:
271 with a dict D and the following conflict policy dict:
271
272
272 S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'})
273 S.merge(D,{fn1:['a','b',4], fn2:'key_c key_d'})
273
274
274 If the key 'a' is found in both S and D, the merge method will call:
275 If the key 'a' is found in both S and D, the merge method will call:
275
276
276 S['a'] = fn1(S['a'],D['a'])
277 S['a'] = fn1(S['a'],D['a'])
277
278
278 As a convenience, merge() provides five (the most commonly needed)
279 As a convenience, merge() provides five (the most commonly needed)
279 pre-defined policies: preserve, update, add, add_flip and add_s. The
280 pre-defined policies: preserve, update, add, add_flip and add_s. The
280 easiest explanation is their implementation:
281 easiest explanation is their implementation:
281
282
282 preserve = lambda old,new: old
283 preserve = lambda old,new: old
283 update = lambda old,new: new
284 update = lambda old,new: new
284 add = lambda old,new: old + new
285 add = lambda old,new: old + new
285 add_flip = lambda old,new: new + old # note change of order!
286 add_flip = lambda old,new: new + old # note change of order!
286 add_s = lambda old,new: old + ' ' + new # only works for strings!
287 add_s = lambda old,new: old + ' ' + new # only works for strings!
287
288
288 You can use those four words (as strings) as keys in conflict instead
289 You can use those four words (as strings) as keys in conflict instead
289 of defining them as functions, and the merge method will substitute
290 of defining them as functions, and the merge method will substitute
290 the appropriate functions for you. That is, the call
291 the appropriate functions for you. That is, the call
291
292
292 S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]})
293 S.merge(D,{'preserve':'a b c','add':[4,5,'d'],my_function:[6]})
293
294
294 will automatically substitute the functions preserve and add for the
295 will automatically substitute the functions preserve and add for the
295 names 'preserve' and 'add' before making any function calls.
296 names 'preserve' and 'add' before making any function calls.
296
297
297 For more complicated conflict resolution policies, you still need to
298 For more complicated conflict resolution policies, you still need to
298 construct your own functions. """
299 construct your own functions. """
299
300
300 data_dict = Struct.__make_dict(self,__loc_data__,**kw)
301 data_dict = Struct.__make_dict(self,__loc_data__,**kw)
301
302
302 # policies for conflict resolution: two argument functions which return
303 # policies for conflict resolution: two argument functions which return
303 # the value that will go in the new struct
304 # the value that will go in the new struct
304 preserve = lambda old,new: old
305 preserve = lambda old,new: old
305 update = lambda old,new: new
306 update = lambda old,new: new
306 add = lambda old,new: old + new
307 add = lambda old,new: old + new
307 add_flip = lambda old,new: new + old # note change of order!
308 add_flip = lambda old,new: new + old # note change of order!
308 add_s = lambda old,new: old + ' ' + new
309 add_s = lambda old,new: old + ' ' + new
309
310
310 # default policy is to keep current keys when there's a conflict
311 # default policy is to keep current keys when there's a conflict
311 conflict_solve = list2dict2(self.keys(),default = preserve)
312 conflict_solve = list2dict2(self.keys(),default = preserve)
312
313
313 # the conflict_solve dictionary is given by the user 'inverted': we
314 # the conflict_solve dictionary is given by the user 'inverted': we
314 # need a name-function mapping, it comes as a function -> names
315 # need a name-function mapping, it comes as a function -> names
315 # dict. Make a local copy (b/c we'll make changes), replace user
316 # dict. Make a local copy (b/c we'll make changes), replace user
316 # strings for the three builtin policies and invert it.
317 # strings for the three builtin policies and invert it.
317 if __conflict_solve:
318 if __conflict_solve:
318 inv_conflict_solve_user = __conflict_solve.copy()
319 inv_conflict_solve_user = __conflict_solve.copy()
319 for name, func in [('preserve',preserve), ('update',update),
320 for name, func in [('preserve',preserve), ('update',update),
320 ('add',add), ('add_flip',add_flip), ('add_s',add_s)]:
321 ('add',add), ('add_flip',add_flip), ('add_s',add_s)]:
321 if name in inv_conflict_solve_user.keys():
322 if name in inv_conflict_solve_user.keys():
322 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
323 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
323 del inv_conflict_solve_user[name]
324 del inv_conflict_solve_user[name]
324 conflict_solve.update(Struct.__dict_invert(self,inv_conflict_solve_user))
325 conflict_solve.update(Struct.__dict_invert(self,inv_conflict_solve_user))
325 #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg
326 #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg
326 #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve)
327 #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve)
327 for key in data_dict:
328 for key in data_dict:
328 if key not in self:
329 if key not in self:
329 self[key] = data_dict[key]
330 self[key] = data_dict[key]
330 else:
331 else:
331 self[key] = conflict_solve[key](self[key],data_dict[key])
332 self[key] = conflict_solve[key](self[key],data_dict[key])
332
333
333 def has_key(self,key):
334 def has_key(self,key):
334 """Like has_key() dictionary method."""
335 """Like has_key() dictionary method."""
335 return self.__dict__.has_key(key)
336 return self.__dict__.has_key(key)
336
337
337 def hasattr(self,key):
338 def hasattr(self,key):
338 """hasattr function available as a method.
339 """hasattr function available as a method.
339
340
340 Implemented like has_key, to make sure that all available keys in the
341 Implemented like has_key, to make sure that all available keys in the
341 internal dictionary of the Struct appear also as attributes (even
342 internal dictionary of the Struct appear also as attributes (even
342 numeric keys)."""
343 numeric keys)."""
343 return self.__dict__.has_key(key)
344 return self.__dict__.has_key(key)
344
345
345 def items(self):
346 def items(self):
346 """Return the items in the Struct's dictionary, in the same format
347 """Return the items in the Struct's dictionary, in the same format
347 as a call to {}.items()."""
348 as a call to {}.items()."""
348 return self.__dict__.items()
349 return self.__dict__.items()
349
350
350 def keys(self):
351 def keys(self):
351 """Return the keys in the Struct's dictionary, in the same format
352 """Return the keys in the Struct's dictionary, in the same format
352 as a call to {}.keys()."""
353 as a call to {}.keys()."""
353 return self.__dict__.keys()
354 return self.__dict__.keys()
354
355
355 def values(self,keys=None):
356 def values(self,keys=None):
356 """Return the values in the Struct's dictionary, in the same format
357 """Return the values in the Struct's dictionary, in the same format
357 as a call to {}.values().
358 as a call to {}.values().
358
359
359 Can be called with an optional argument keys, which must be a list or
360 Can be called with an optional argument keys, which must be a list or
360 tuple of keys. In this case it returns only the values corresponding
361 tuple of keys. In this case it returns only the values corresponding
361 to those keys (allowing a form of 'slicing' for Structs)."""
362 to those keys (allowing a form of 'slicing' for Structs)."""
362 if not keys:
363 if not keys:
363 return self.__dict__.values()
364 return self.__dict__.values()
364 else:
365 else:
365 ret=[]
366 ret=[]
366 for k in keys:
367 for k in keys:
367 ret.append(self[k])
368 ret.append(self[k])
368 return ret
369 return ret
369
370
370 def get(self,attr,val=None):
371 def get(self,attr,val=None):
371 """S.get(k[,d]) -> S[k] if S.has_key(k), else d. d defaults to None."""
372 """S.get(k[,d]) -> S[k] if S.has_key(k), else d. d defaults to None."""
372 try:
373 try:
373 return self[attr]
374 return self[attr]
374 except KeyError:
375 except KeyError:
375 return val
376 return val
376
377
377 def setdefault(self,attr,val=None):
378 def setdefault(self,attr,val=None):
378 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if not S.has_key(k)"""
379 """S.setdefault(k[,d]) -> S.get(k,d), also set S[k]=d if not S.has_key(k)"""
379 if not self.has_key(attr):
380 if not self.has_key(attr):
380 self[attr] = val
381 self[attr] = val
381 return self.get(attr,val)
382 return self.get(attr,val)
382
383
383 def allow_new_attr(self, allow = True):
384 def allow_new_attr(self, allow = True):
384 """ Set whether new attributes can be created inside struct
385 """ Set whether new attributes can be created inside struct
385
386
386 This can be used to catch typos by verifying that the attribute user tries to
387 This can be used to catch typos by verifying that the attribute user tries to
387 change already exists in this Struct.
388 change already exists in this Struct.
388 """
389 """
389 self['__allownew'] = allow
390 self['__allownew'] = allow
390
391
391
392
392 # end class Struct
393 # end class Struct
393
394
General Comments 0
You need to be logged in to leave comments. Login now