[Python-Dev] PEP 362: 4th edition (original) (raw)
Jim J. Jewett jimjjewett at gmail.com
Sat Jun 16 05:56:31 CEST 2012
- Previous message: [Python-Dev] PEP 362: 4th edition
- Next message: [Python-Dev] PEP 362: 4th edition
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Summary:
*Every* Parameter attribute is optional, even name. (Think of
builtins, even if they aren't automatically supported yet.)
So go ahead and define some others that are sometimes useful.
Instead of defining a BoundArguments class, just return a copy
of the Signature, with "value" attributes added to the Parameters.
Use subclasses to distinguish the parameter kind. (Replacing
most of the is_ methods from the 3rd version.)
[is_]implemented is important information, but the API isn't
quite right; even with tweaks, maybe we should wait a version
before freezing it on the base class. But I would be happy
to have Larry create a Signature for the os.* functions,
whether that means a subclass or just an extra instance
attribute.
I favor passing a class to Signature.format, because so many of
the formatting arguments would normally change in parallel.
But my tolerance for nested structures may be unusually high.
I make some more specific suggestions below.
In http://mail.python.org/pipermail/python-dev/2012-June/120305.html Yury Selivanov wrote:
A Signature object has the following public attributes and methods:
* returnannotation : object The annotation for the return type of the function if specified. If the function has no annotation for its return type, this attribute is not set.
This means users must already be prepared to use hasattr with the Signature as well as the Parameters -- in which case, I don't see any harm in a few extra optional properties.
I would personally prefer to see the name (and qualname) and docstring, but it would make perfect sense to implement these by keeping a weakref to the original callable, and just delegating there unless/until the properties are explicitly changed. I suspect others will have a use for additional delegated attributes, such as the self of boundmethods.
I do agree that eq and hash should depend at most on the parameters (including their order) and the annotation.
* parameters : OrderedDict An ordered mapping of parameters' names to the corresponding Parameter objects (keyword-only arguments are in the same order as listed in
code.covarnames
).
For a specification, that feels a little too tied to the specific implementation. How about:
Parameters will be ordered as they are in the function declaration.
or even just:
Positional parameters will be ordered as they are in the function
declaration.
because: def f(*, a=4, b=5): pass
and: def f(*, b=5, a=4): pass
should probably have equal signatures.
Wild thought: Instead of just having an OrderedDict of Parameters, should a Signature be that OrderedDict (with other attributes)? That is, should signature(testfn)["foo"] get the foo parameter?
* bind(*args, **kwargs) -> BoundArguments Creates a mapping from positional and keyword arguments to parameters. Raises a
BindError
(subclass ofTypeError
) if the passed arguments do not match the signature. * bindpartial(*args, **kwargs) -> BoundArguments Works the same way asbind()
, but allows the omission of some required arguments (mimicsfunctools.partial
behavior.)
Are those descriptions actually correct?
I would expect the mapping to be from parameters (or parameter names) to values extracted from *args and **kwargs.
And I'm not sure the current patch does even that, since it seems to instead return a non-Mapping object (but with a mapping attribute) that could be used to re-create *args, **kwargs in canonical form. (Though that canonicalization is valuable for calls; it might even be worth an as_call method.)
I think it should be explicit that this mapping does not include parameters which would be filled by default arguments. In fact, if you stick with this interface, I would like a 3rd method that does fill out everything.
But I think it would be simpler to just add an optional attribute to each Parameter instance, and let bind fill that in on the copies, so that the return value is also a Signature. (No need for the BoundArguments class.) Then the user can decide whether or not to plug in the defaults for missing values.
* format(...) -> str Formats the Signature object to a string. Optional arguments allow for custom render functions for parameter names, annotations and default values, along with custom separators.
I think it should state explicitly that by default, the return value will be a string that could be used to declare an equivalent function, if "Signature" were replaced with "def funcname".
There are enough customization parameters that would often be changed together (e.g., to produce HTML output) that it might make sense to use overridable class defaults -- or even to make format a class itself.
I also think it would make sense to delegate formatting the individual parameters to the parameter objects. Yes, this does mean that the subclasses would be more than markers classes.
It's possible to test Signatures for equality. Two signatures are equal when they have equal parameters and return annotations.
I would be more explicit about parameter order mattering. Perhaps:
It's possible to test Signatures for equality. Two signatures are
equal when their parameters are equal, their positional parameters
appear in the same order, and they have equal return annotations.
The structure of the Parameter object is:
* name : str The name of the parameter as a string.
If there is no name, as with some builtins, will this be "", None or not set?
(3rd edition)
* iskeywordonly : bool ... * isargs : bool ... * iskwargs : bool ...
(4th edition)
... Parameter.POSITIONALONLY ... ... Parameter.POSITIONALORKEYWORD ... ... Parameter.KEYWORDONLY ... ... Parameter.VARPOSITIONAL ... ... Parameter.VARKEYWORD ...
This set has already grown, and I can think of others I would like to use. (Pseudo-parameters, such as a method's self instance, or an auxiliary variable.)
To me, that suggests marker subclasses and isinstance checking.
class BaseParameter: ...
# These two really are different
class ArgsParameter(BaseParameter): ...
class KwargsParameter(BaseParameter): ...
class KeywordParameter(BaseParameter): ...
class PositionalParameter(BaseParameter): ...
class Parameter(KeywordParameter, PositionalParameter): ...
(I'm not sure that normal parameters should really be the bottom of an inheritance diamond, as opposed to a sibling.)
It may also be worth distinguishing BoundParameter at the class level, but since the only distinction is an extra attribute (value), I'm not sure about that.
Question: These names are getting long. Was there a reason not to use Sig and Param?
* implemented : bool
This information is valuable, and should be at least an optional part of a signature (or a specific parameter, if there are no interactions). But I don't think a boolean is sufficient.
At a minimum, let it return an object such that bool(obj) indicates whether non-default values are ever useful, but obj could provide more information. For example I would be happy with the following:
>>> s=signature(open("myfile").seek)
>>> v=s.parameters["whence"].is_implemented
>>> v[0]
True
>>> v["SEEK_DATA"]
False
That said, if the decision is to leave is_implemented up to subclasses for now, I won't object.
Two parameters are equal when all their attributes are equal.
I think that it would be better to specify which attributes matter, and let them be equal so long as those attributes matched. I'll try to post details on the ticket, but roughly only the attributes specifically mentioned in this PEP should matter. I'm not sure if positional parameters should also check position, or if that can be left to the Signature.
-jJ
--
If there are still threading problems with my replies, please email me with details, so that I can try to resolve them. -jJ
- Previous message: [Python-Dev] PEP 362: 4th edition
- Next message: [Python-Dev] PEP 362: 4th edition
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]