Conditions | 22 |
Total Lines | 115 |
Lines | 0 |
Ratio | 0 % |
Changes | 14 | ||
Bugs | 0 | Features | 0 |
Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.
For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.
Commonly applied refactorings include:
If many parameters/temporary variables are present:
Complex classes like variadic() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | #!/usr/bin/env python |
||
114 | def variadic(typ): |
||
115 | """ |
||
116 | Decorator taking a variadic function and making a very-variadic function from it: |
||
117 | a function that can be called with a variable number of iterables of arguments. |
||
118 | |||
119 | :param typ: the type (or tuple of types) of arguments expected. |
||
120 | Variadic arguments that are instances of this type will be passed to the decorated function as-is. |
||
121 | Others will be iterated and their contents will be passed. |
||
122 | """ |
||
123 | def flatten(args): |
||
124 | flat = [] |
||
125 | for arg in args: |
||
126 | if isinstance(arg, typ): |
||
127 | flat.append((arg,)) |
||
128 | else: |
||
129 | flat.append(arg) |
||
130 | return itertools.chain.from_iterable(flat) |
||
131 | |||
132 | def decorator(wrapped): |
||
133 | if sys.hexversion < 0x03000000: |
||
134 | spec = inspect.getargspec(wrapped) |
||
135 | assert spec.varargs is not None |
||
136 | varargs = spec.varargs |
||
137 | keywords = spec.keywords |
||
138 | else: |
||
139 | spec = inspect.getfullargspec(wrapped) |
||
140 | assert spec.varargs is not None |
||
141 | assert spec.kwonlyargs == [] |
||
142 | assert spec.kwonlydefaults is None |
||
143 | assert spec.annotations == {} |
||
144 | varargs = spec.varargs |
||
145 | keywords = spec.varkw |
||
146 | |||
147 | name = wrapped.__name__ |
||
148 | |||
149 | # Example was generated with print ast.dump(ast.parse("def f(a, b, *args, **kwds): |
||
150 | # return call_wrapped((a, b), *args, **kwds)"), include_attributes=True) |
||
151 | # http://code.activestate.com/recipes/578353-code-to-source-and-back/ helped a lot |
||
152 | # http://stackoverflow.com/questions/10303248#29927459 |
||
153 | |||
154 | if sys.hexversion < 0x03000000: |
||
155 | wrapper_ast_args = ast.arguments( |
||
156 | args=[ast.Name(id=a, ctx=ast.Param(), lineno=1, col_offset=0) for a in spec.args], |
||
157 | vararg=varargs, |
||
158 | kwarg=keywords, |
||
159 | defaults=[] |
||
160 | ) |
||
161 | else: |
||
162 | wrapper_ast_args = ast.arguments( |
||
163 | args=[ast.arg(arg=a, annotation=None, lineno=1, col_offset=0) for a in spec.args], |
||
164 | vararg=( |
||
165 | None if varargs is None else |
||
166 | ast.arg(arg=varargs, annotation=None, lineno=1, col_offset=0) |
||
167 | ), |
||
168 | kwonlyargs=[], |
||
169 | kw_defaults=[], |
||
170 | kwarg=( |
||
171 | None if keywords is None else |
||
172 | ast.arg(arg=keywords, annotation=None, lineno=1, col_offset=0) |
||
173 | ), |
||
174 | defaults=[] |
||
175 | ) |
||
176 | |||
177 | wrapped_func = ast.Name(id="wrapped", ctx=ast.Load(), lineno=1, col_offset=0) |
||
178 | wrapped_args = [ast.Name(id=a, ctx=ast.Load(), lineno=1, col_offset=0) for a in spec.args] |
||
179 | flatten_func = ast.Name(id="flatten", ctx=ast.Load(), lineno=1, col_offset=0) |
||
180 | flatten_args = [ast.Name(id=spec.varargs, ctx=ast.Load(), lineno=1, col_offset=0)] |
||
181 | |||
182 | if sys.hexversion < 0x03050000: |
||
183 | return_value = ast.Call( |
||
184 | func=wrapped_func, |
||
185 | args=wrapped_args, |
||
186 | keywords=[], |
||
187 | starargs=ast.Call( |
||
188 | func=flatten_func, args=flatten_args, |
||
189 | keywords=[], starargs=None, kwargs=None, lineno=1, col_offset=0 |
||
190 | ), |
||
191 | kwargs=( |
||
192 | None if keywords is None else |
||
193 | ast.Name(id=keywords, ctx=ast.Load(), lineno=1, col_offset=0) |
||
194 | ), |
||
195 | lineno=1, col_offset=0 |
||
196 | ) |
||
197 | else: |
||
198 | return_value = ast.Call( |
||
199 | func=wrapped_func, |
||
200 | args=wrapped_args + [ |
||
201 | ast.Starred( |
||
202 | value=ast.Call(func=flatten_func, args=flatten_args, keywords=[], lineno=1, col_offset=0), |
||
203 | ctx=ast.Load(), lineno=1, col_offset=0 |
||
204 | ), |
||
205 | ], |
||
206 | keywords=( |
||
207 | [] if keywords is None else |
||
208 | [ast.keyword(arg=None, value=ast.Name(id=keywords, ctx=ast.Load(), lineno=1, col_offset=0))] |
||
209 | ), |
||
210 | lineno=1, col_offset=0 |
||
211 | ) |
||
212 | |||
213 | wrapper_ast = ast.Module(body=[ast.FunctionDef( |
||
214 | name=name, |
||
215 | args=wrapper_ast_args, |
||
216 | body=[ast.Return(value=return_value, lineno=1, col_offset=0)], |
||
217 | decorator_list=[], |
||
218 | lineno=1, |
||
219 | col_offset=0 |
||
220 | )]) |
||
221 | wrapper_code = [ |
||
222 | c for c in compile(wrapper_ast, "<ast_in_variadic_py>", "exec").co_consts if isinstance(c, types.CodeType) |
||
223 | ][0] |
||
224 | wrapper = types.FunctionType(wrapper_code, {"wrapped": wrapped, "flatten": flatten}, argdefs=spec.defaults) |
||
225 | |||
226 | functools.update_wrapper(wrapper, wrapped) |
||
227 | return wrapper |
||
228 | return decorator |
||
229 | |||
344 |