オートコンプリートは、ほとんどの場合、フックできるdir()
関数の出力を利用します。__dir__()
メソッドを実装するだけです。
def __dir__(self):
res = dir(type(self)) + list(self.__dict__.keys())
res.extend(['dynamic1', 'dynamic2'])
return res
署名を一致させながら関数をラップする場合は、その署名に基づいてファサードを作成する必要があります。私はZopeのセキュリティ機能のためにまさにそれをしました:
import inspect
import functools
class _Default(object):
def __init__(self, repr):
self._repr = repr
def __repr__(self):
return self._repr
def _buildFacade(name, spec, docstring):
"""Build a facade function, matching the decorated method in signature.
Note that defaults are replaced by instances of _Default, and _curried
will reconstruct these to preserve mutable defaults.
"""
args = inspect.formatargspec(
formatvalue=lambda v: '=_Default({0!r})'.format(repr(v)), *spec)
callargs = inspect.formatargspec(formatvalue=lambda v: '', *spec)
return 'def {0}{1}:\n """{2}"""\n return _curried{3}'.format(
name, args, docstring, callargs)
def add_docs(tool):
spec = inspect.getargspec(tool)
args, defaults = spec[0], spec[3]
arglen = len(args)
if defaults is not None:
defaults = zip(args[arglen - len(defaults):], defaults)
arglen -= len(defaults)
def _curried(*args, **kw):
# Reconstruct keyword arguments
if defaults is not None:
args, kwparams = args[:arglen], args[arglen:]
for positional, (key, default) in zip(kwparams, defaults):
if isinstance(positional, _Default):
kw[key] = default
else:
kw[key] = positional
return tool(*args, **kw)
name = tool.__name__
doc = 'Showing help for {0}()'.format(name)
facade_globs = dict(_curried=_curried, _Default=_Default)
exec _buildFacade(name, spec, doc) in facade_globs
wrapped = facade_globs[name]
wrapped = functools.update_wrapper(wrapped, tool,
assigned=filter(lambda w: w != '__doc__', functools.WRAPPER_ASSIGNMENTS))
return facade_globs[name]
これは、メソッドのシグネチャに関しては、ほぼ正しいことを行います。ここでは変更可能なデフォルトを回避することはできず、それらを保持するために明示的に処理する必要があります。
小さなデモンストレーション:
>>> def foo(bar, spam='eggs', foobarred={}):
... foobarred[bar] = spam
... print foobarred
...
>>> documented = add_docs(foo)
>>> help(documented)
Help on function foo:
foo(bar, spam='eggs', foobarred={})
Showing help for foo()
>>> documented('monty', 'python')
{'monty': 'python'}
>>> documented('Eric', 'Idle')
{'Eric': 'Idle', 'monty': 'python'}
ダンス全体_Default
は、変更可能なデフォルトを維持するために必要です。これは、一般的には悪い考えですが、当初の意図どおりに機能し続ける必要があります。構築されたファサードは元の外観と同じように見え、そのように機能しますが、可変は「正しい」場所に存在し続けます。
ファサードは、オリジナルと可能な限り一致するように更新されることに注意してください。さまざまなメタデータを使用して、元のメタデータからファサードにコピーされますが、ファサードは代わりに独自のdocstringを明示的に使用するため、文字列functools.update_wrapper
を除外するように注意します。__doc__