[Python-Dev] PEP 362: 4th edition

Yury Selivanov yselivanov.ml at gmail.com
Tue Jun 19 00:54:54 CEST 2012


Jim,

On 2012-06-18, at 5:06 PM, Jim Jewett wrote:
> On Mon, Jun 18, 2012 at 10:37 AM, Yury Selivanov
> <yselivanov.ml at gmail.com> wrote:
>> Jim,
>> 
>> On 2012-06-18, at 3:08 AM, Jim Jewett wrote:
>>> On Sat, Jun 16, 2012 at 11:27 AM, Nick Coghlan <ncoghlan at gmail.com> wrote:
>>>> On Sat, Jun 16, 2012 at 1:56 PM, Jim J. Jewett <jimjjewett at gmail.com> wrote:
> 
>>>>>    Instead of defining a BoundArguments class, just return
>>>>>   a copy  of the Signature, with "value" attributes added to
>>>>>   the Parameters.
> 
>>>> No, the "BoundArguments" class is designed to be easy to
>>>> feed to a function call as f(*args, **kwds)
> 
>>> Why does that take a full class, as opposed to a method returning a
>>> tuple and a dict?
> 
>> Read this thread, please: http://mail.python.org/pipermail/python-dev/2012-June/120000.html
> 
> I reread that.  I still don't see why it needs to be an instance of a
> specific independent class, as opposed to a Signature method that
> returns a (tuple of) a tuple and a dict.
> 
>   ((arg1, arg2, arg3...), {key1: val2, key2: val2})

I'll try to explain with the code:

    def foo(a, *args, b, **kwargs): pass
    sig = signature(foo)

* Case one.  We have BoundArguments:

    ba = sig.bind(1, 2, 3, b=123, c='foo', d='bar')

Now, in 'ba.arguments':
   
    {'a': 1, 'args': (2, 3), 'b': 123, 'kwargs': {'c': 'foo', 'd': 'bar'}}

It's easy to work with 'ba.arguments', just traverse it as follows:

    for arg_name, arg_value in ba.arguments:
        param = sig.parameters[arg_name]

So you have argument name and value, and the corresponding parameter.

* Case two.  We return tuple and dict:

    ba = sig.bind(1, 2, 3, b=123, c='foo', d='bar')
    ((1, 2, 3), {'b': 123, 'c': 'foo', 'd': 'bar'})

Now, how are you going to work with that?  How will you map those
values to the corresponding parameters?  The whole point of having
'Signature.bind()' is to provide you that mapping.

In the link I gave you, I also explained why we need 'BoundArguments.args'
and 'BoundArguments.kwargs'.

>>>>>    Use subclasses to distinguish the parameter kind.
> 
>>>> Please, no, using subclasses when there is no behavioural
>>>> change is annoying.
> 
> [Examples of how the "kinds" of parameters are qualitatively different.]
> 
>>> A **kwargs argument is very different from an ordinary parameter.  Its
>>> name doesn't matter (and therefore should not be considered in
>>> __eq__),
> 
>> The importance of its name depends hugely on the use context.  In some
>> it may be very important.
> 
> The name of kwargs can only be for documentation purposes.

Not really.  I've seen once the code where types of acceptable values
were encoded parameter names (after '__', like 'kwargs__int').
This is a rather extreme example, but it illustrates that names
may be important.

>  Like an
> annotation or a docstring, it won't affect the success of an attempted
> call.  

It very well may affect it.  For instance, I use annotations to
specify arguments types in RPC dispatch.  The call will not be dispatched
in case of a wrong type.  So 'foo(a:int)' in my context does not
equal to 'foo(a:str)'.

>> And it is treated specially, along with the *args.
> 
> Right -- but this was in response to Nick's claim that the
> distinctions should not be represented as a subclass, because the
> behavior wasn't different.

Yes, it's behaviour of the outer code (Signature) that is different, not 
the Parameter instances.  It's Signature who makes a decision of how to map
parameters, not parameters themselves.

> I consider different __eq__ implementations or formatting concers to
> be sufficient on their own; I also consider different possible use
> locations and counts, different used-by-the-system attributes (name),
> or different value types (object vs collection) to be sufficiently
> behavioral.
> 
>>>>>> A Signature object has the following public attributes and methods:
> 
>>> The more I try to work with it, the more I want direct references to
>>> the two special arguments (*args, **kwargs) if they exist.  FWIW, the
>>> current bind logic to find them -- particularly kwargs -- seems
>>> contorted, compared to self.kwargsparameter.
> 
>> Well, 'self.kwargsparameter'  will break 'self.parameters' collection,
>> unless you want one parameter to be in two places.
> 
> Correct; it should be redundant.  Signature.kwargsparameter should be
> the same object that occurs as the nth element of
> Signature.parameters.values().  

It will be always the last parameter (if specified at all)

> It is just more convenient to retrieve
> the parameter directly than it is to iterate through a collection
> inspecting each element for the value of a specific attribute.

Again, depends on the use case.  Can you show a realistic use case,
that needs that?  (The use case should be also common and widespread, 
i.e. it should worth it to uglify Signature class structure)

>> In fact, the check types example (in the PEP) is currently shorter and
>> easier to read with 'Signature.parameters' than with dedicated property
>> for '**kwargs' parameter.
> 
> Agreed; the short-cuts *args and **kwargs are only useful because they
> are special; they aren't needed when you're doing the same thing to
> all parameters regardless of type.
> 
>> And if after all you need direct references to *args or **kwargs - write
>> a little helper, which finds them in 'Signature.parameters'.
> 
> Looking at http://bugs.python.org/review/15008/diff/5143/Lib/inspect.py
> you already need one in _bind; it is just that saving the info when
> you pass it isn't too bad if you're already iterating through the
> whole collection anyhow.

Having a 'Signature.kwargs_param' would save just 2 lines of code
(1736:'kwargs_param = None', and 1745:'kwargs_param = param') out of 120.
And that's BTW is a pretty complex piece of code.  

Again, it all depends on the use-case.

>>>  Also,
>>> positional_only, *args, and **kwargs should be able to remove name
>>> from the list of compared attributes.
> 
>> I still believe in the most contexts the name of a parameter matters
>> (even if it's **kwargs).  Besides, how can we make __eq__ to be
>> configurable?
> 
> __eq__ can can an _eq_fields attribute to see which other attributes
> matter -- but it makes more sense for that to be (sub-) class
> property.

Well we can make __eq__ customizable, but I'm afraid it will just
complicate things too much.  It should take you 10-20 lines of code to
implement any comparison algorithm you want - why try to predict all
of them and put in the stdlib?

Thank you,
-
Yury


More information about the Python-Dev mailing list