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