On Mon, Jun 18, 2012 at 5:08 PM, Jim Jewett <jimjjew...@gmail.com> wrote: > On Sat, Jun 16, 2012 at 11:27 AM, Nick Coghlan <ncogh...@gmail.com> wrote: >> No. This is the full set of binding behaviours. "self" is just an >> ordinary "POSITIONAL_OR_KEYWORD" argument (or POSITIONAL_ONLY, in some >> builtin cases). > > Or no longer a "parameter" at all, once the method is bound. Except > it sort of still is. Same for the space parameter in PyPy. I don't > expect the stdlib implementation to support them initially, but I > don't want it to get in the way, either. A supposedly closed set gets > in the way.
It's not supposedly closed, it *is* closed: Python doesn't support any other ways of binding arguments to parameters. Now, you can have additional state on a callable that gets used by that callable (such as __closure__ and __globals__ on a function, or __self__ on a method, or arbitrary state on an object that implements __call__) but that extra state is not part of the call *signature*, and thus should not be exposed on the result of inspect.getsignature(). Remember, this object is not meant to be a representation of the full state of a callable, it's solely about providing a consistent introspection mechanism that allows arbitrary callables to define how they will bind arguments to parameters. That's why I keep pointing out that there will always need to be a higher level object that brings in other related information, such as the docstring, the name of the callable, etc. This is not an API that describes *everything* that is worth knowing about an arbitrary callable, nor is it intended to be. I believe you have raised a legitimate question regarding whether or not there is sufficient variation in behaviour amongst the parameter kinds for it to be worth using a subclassing approach to override __str__ and __eq__, compared to just implementing a few "kind" checks in the base implementation. However, given that the possible binding behaviours (positional only, keyword-or-positional, excess positional, keyword-only, excess keywords) *is* a closed set, I think a subclass based solution is overkill and adds excessive complexity to the public API. Cheers, Nick. P.S. A more complete response to the question of what constitutes "suitable complexity" for this API. Consider the following implementation sketch for a kind based implementation that pushes as much behaviour as is reasonable into the Parameter object (including the definition of equality and decisions on how the parameter should be displayed): _sentinel = object() class Parameter: def __init__(self, name, kind, default=_sentinel, annotation=_sentinel): if not name: raise ValueError("All parameters must be named for introspection purposes (even positional-only parameters)") self.name = name if kind not in Parameter.KINDS: raise ValueError("Unrecognised parameter binding type {}".format(kind)) self.kind = kind if default is not _sentinel: if kind.startswith("VAR"): raise ValueError("Cannot specify default value for {} parameter".format(kind)) self.default = default if annotation is not _sentinel: self.annotation = annotation def _get_key(self): default = getattr(self, "default", _sentinel) annotation = getattr(self, "annotation", _sentinel) if self.kind in (Parameter.KEYWORD_OR_POSITIONAL, Parameter.KEYWORD_ONLY): # The name is considered significant for parameters that can be specified # as keyword arguments return (self.name, self.kind, default, annotation) # Otherwise, we don't really care about the name return (self.kind, default, annotation) def __eq__(self, other): if not isinstance(other, Parameter): return NotImplemented return self._get_key() == other._get_key() def __str__(self): kind = self.kind components = [] if kind == Parameter.VAR_POSITIONAL: components += ["*"] elif kind == Parameter.VAR_KEYWORD: components += ["**"] if kind == Parameter.POSITIONAL_ONLY: components += ["<", self.name, ">"] else: components += [self.name] try: default = self.default except AttributeError: pass else: components += ["=", repr(default)] try: annotation = self.annotation except AttributeError: pass else: components += [":", repr(annotation)] return "".join(components) The points of variation: - VAR_POSITIONAL and VAR_KEYWORD do not permit a "default" attribute - VAR_POSITIONAL adds a "*" before the name when printed out - VAR_KEYWORD adds a "**" before the name when printed out - POSITIONAL_ONLY adds "<>" around the name when printed out - all three of those ignore "name" for comparisons Now, suppose we dispense with the kind attribute and use subclassing instead: _sentinel = object() class _ParameterBase: """Common behaviour for all parameter types""" def __init__(self, name, default=_sentinel, annotation=_sentinel): if not name: raise ValueError("All parameters must be named for introspection purposes") self.name = name self.kind = kind if default is not _sentinel: self.default = default if annotation is not _sentinel: self.annotation = annotation def _get_key(self): default = getattr(self, "default", _sentinel) annotation = getattr(self, "annotation", _sentinel) return (self.name, default, annotation) def __eq__(self, other): if not isinstance(other, type(self)): return NotImplemented return self._get_key() == other._get_key() def __str__(self): components = [self.name] try: default = self.default except AttributeError: pass else: components += ["=", repr(default)] try: annotation = self.annotation except AttributeError: pass else: components += [":", repr(annotation)] return "".join(components) class Parameter(_ParameterBase): """Representation of a normal Python function parameter""" class KeywordOnlyParameter(_ParameterBase): """Representation of a keyword-only Python function parameter""" class PositionalOnlyParameter(_ParameterBase): """Representation of a positional-only parameter""" def __init__(self, index, name=None, default=_sentinel, annotation=_sentinel): if name is not None: display_name = "<{}:{}>".format(index, name) else: display_name = "<{}>".format(index) super().__init__(display_name, default, annotation) def _get_key(self): default = getattr(self, "default", _sentinel) annotation = getattr(self, "annotation", _sentinel) return (default, annotation) class _VarParameterBase(_ParameterBase): """Common behaviour for variable argument parameters""" class VarPositionalParameter(_VarParameterBase): """Representation of a parameter for excess positional arguments""" def __str__(self): return "*" + super().__str__() class VarKeywordParameter(_VarParameterBase): """Representation of a parameter for excess keyword arguments""" def __str__(self): return "**" + super().__str__() Cheers, Nick. -- Nick Coghlan | ncogh...@gmail.com | Brisbane, Australia _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com