I'd likely propose doing both as properties so that we don't need to hold
onto either of them... I was trying this out when I hit:

```
C:\Users\csm10495\Desktop\cpython\Scripts>ipython
Could not import runpy module
Traceback (most recent call last):
  File "<frozen importlib._bootstrap>", line 1172, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1143, in
_find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 684, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 976, in exec_module
  File "<frozen runpy>", line 15, in <module>
  File "<frozen importlib._bootstrap>", line 1172, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1143, in
_find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 684, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 976, in exec_module
  File "<frozen importlib.util>", line 14, in <module>
  File "C:\Users\csm10495\Desktop\cpython\Lib\contextlib.py", line 7, in
<module>
    from functools import wraps
  File "C:\Users\csm10495\Desktop\cpython\Lib\functools.py", line 276, in
<module>
    class partial:
TypeError: type __qualname__ must be a str, not property
```

... which is https://bugs.python.org/issue19073..

But for __name__ it seems to work:

```
In [1]: from functools import partial

In [2]: from sys import getsizeof

In [3]: hello = partial(print, "hello")

In [4]: getsizeof(hello)
Out[4]: 72

In [5]: hello.__name__
Out[5]: 'partial(print)'
```
and for the simple test, added this to the partial definition:

```
    @property
    def __name__(self):
        return f"partial({self.func.__name__})"
```
Of course it probably wouldn't be too bad to add in the args/kwargs to
the __name__ (and eventually __qualname__) as well.

This was with commenting out using the C-version of partial and just
playing with the Python version. If folks think this (cleaned up) could
make upstream, I'd be happy to try to get a PR together for first allowing
__qualname__ to be a property then what we're talking about here.

- Charlie Scott Machalow


On Tue, Aug 30, 2022 at 7:08 PM Joao S. O. Bueno <gwid...@gmail.com> wrote:

> Actually, there is a good motive IMO for a partial function to have
> __name__ and __qualname__: the code one is passing a partial function
> might expect these attributes to be presented in the callable it get.
>
> It is just a matter of unifying the interface for callables that are often
> used as arguments in calls, and as such, even
> if __name__ and __qualname__ are fixed and immutable strings, this would
> be an improvement.
> (say, a partial callable __name__ could be fixed to "<partial>" just as a
> lambda's __name__ is  "<lambda>")
>
> On Tue, Aug 30, 2022 at 4:29 PM Wes Turner <wes.tur...@gmail.com> wrote:
>
>> Would a property or a copy be faster for existing and possible use cases?
>> In practice, how frequently will __qual/name__ be called on partials?
>>
>> - Copying __qual/name__ would definitely be a performance regression
>>
>> - There are probably as many use cases for partials as other methods of
>> functional composition,
>> - __qual/name__ support is not yet extant
>>
>> - it's faster to run e.g. a grid search *without* partials, due to
>> function call overhead, due to scope allocation on the stack in stackful
>> pythons [1]
>>
>> [1] Hyper Parameter Search > Scaling hyperparameter searches
>>
>> https://ml.dask.org/hyper-parameter-search.html#scaling-hyperparameter-searches
>>
>> [2] Pipeline caching in TPOT
>> http://epistasislab.github.io/tpot/using/#pipeline-caching-in-tpot
>> #parallel-training-with-dask ; TPOT generates  actual python source code
>> instead of an ensemble of partials
>>
>>
>>
>>
>>
>> On Tue, Aug 30, 2022, 12:07 PM Charles Machalow <csm10...@gmail.com>
>> wrote:
>>
>>> We may be able to do __name__/__qualname__ as a property to make it
>>> evaluate when called as opposed to computed once on creation. That way we
>>> just work with .func upon call so no need for extra references, etc.
>>>
>>> As for documentation generation tools, it may be different at first,
>>> though I believe the existing ispartial checks would catch partials still.
>>> If they want to (in a new version) swap to using __name__/__qualname__ that
>>> should be fine, but this likely wouldn't inherently break existing tools.
>>>
>>> - Charlie Scott Machalow
>>>
>>>
>>> On Mon, Aug 29, 2022 at 11:08 PM Wes Turner <wes.tur...@gmail.com>
>>> wrote:
>>>
>>>> Is there a non-performance regressive way to proxy attr access to
>>>> func.__name__ of the partial function (or method; Callable)?
>>>>
>>>> Would this affect documentation generation tools like e.g.
>>>> sphinx-spidoc, which IIRC use __name__ and probably now __qualname__ for
>>>> generating argspecs in RST for HTML and LaTeX?
>>>>
>>>>
>>>> - https://docs.python.org/3/library/inspect.html
>>>>   - functions and methods have __name__ and __qualname__
>>>>   - see: sphinx.utils.inspect
>>>>
>>>> - https://docs.python.org/3/library/functools.html#functools.partial
>>>> -
>>>> https://docs.python.org/3/library/functools.html#functools.partialmethod
>>>> - https://docs.python.org/3/library/functools.html#partial-objects
>>>>
>>>> > partial Objects¶
>>>> > partial objects are callable objects created by partial(). They have
>>>> three read-only attributes:
>>>> >
>>>> > partial.func
>>>> > A callable object or function. Calls to the partial object will be
>>>> forwarded to func with new arguments and keywords.
>>>> >
>>>> > partial.args
>>>> > The leftmost positional arguments that will be prepended to the
>>>> positional arguments provided to a partial object call.
>>>> >
>>>> > partial.keywords
>>>> > The keyword arguments that will be supplied when the partial object
>>>> is called.
>>>>
>>>> > partial objects are like function objects in that they are callable,
>>>> weak referencable, and can have attributes. There are some important
>>>> differences. For instance, the __name__ and __doc__ attributes are not
>>>> created automatically. Also, partial objects defined in classes behave like
>>>> static methods and do not transform into bound methods during instance
>>>> attribute look-up.
>>>>
>>>>
>>>> - https://www.sphinx-doc.org/en/master/man/sphinx-apidoc.html
>>>> - https://www.sphinx-doc.org/en/master/_modules/sphinx/ext/autodoc.html
>>>> : 18 references to __qualname__,
>>>>
>>>>
>>>> https://github.com/sphinx-doc/sphinx/blob/5.x/sphinx/util/inspect.py#L49-L66
>>>> :
>>>>
>>>> ```python
>>>> def unwrap_all(obj: Any, *, stop: Optional[Callable] = None) -> Any:
>>>>     """
>>>>     Get an original object from wrapped object (unwrapping partials,
>>>> wrapped
>>>>     functions, and other decorators).
>>>>     """
>>>>     while True:
>>>>         if stop and stop(obj):
>>>>             return obj
>>>>         elif ispartial(obj):
>>>>             obj = obj.func
>>>>         elif inspect.isroutine(obj) and hasattr(obj, '__wrapped__'):
>>>>             obj = obj.__wrapped__  # type: ignore
>>>>         elif isclassmethod(obj):
>>>>             obj = obj.__func__
>>>>         elif isstaticmethod(obj):
>>>>             obj = obj.__func__
>>>>         else:
>>>>             return obj
>>>> ```
>>>>
>>>> From
>>>> https://github.com/sphinx-doc/sphinx/blob/5.x/sphinx/util/inspect.py#L173-L186
>>>> :
>>>>
>>>> ```python
>>>> def unpartial(obj: Any) -> Any:
>>>>     """Get an original object from partial object.
>>>>     This returns given object itself if not partial.
>>>>     """
>>>>     while ispartial(obj):
>>>>         obj = obj.func
>>>>
>>>>     return obj
>>>>
>>>>
>>>> def ispartial(obj: Any) -> bool:
>>>>     """Check if the object is partial."""
>>>>     return isinstance(obj, (partial, partialmethod))
>>>> ```
>>>>
>>>> -
>>>> https://github.com/sphinx-doc/sphinx/issues/4826#issuecomment-808699254
>>>>
>>>>
>>>> https://docs.python.org/3/library/stdtypes.html#definition.__name__
>>>>
>>>>
>>>> https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy
>>>> > Callable types > User defined functions does list both __name__ and
>>>> __qualname__
>>>>
>>>> Is there a non-performance regressive way to proxy attr access to
>>>> __name__ of the partially curried (?) function?
>>>>
>>>> From "PEP 3155 – Qualified name for classes and functions"
>>>> https://peps.python.org/pep-3155/#limitations :
>>>>
>>>> > ### Limitations
>>>> > With nested functions (and classes defined inside functions), the
>>>> dotted path will not be walkable programmatically as a function’s namespace
>>>> is not available from the outside. It will still be more helpful to the
>>>> human reader than the bare __name__.
>>>> >
>>>> > As the __name__ attribute, the __qualname__ attribute is computed
>>>> statically and it will not automatically follow rebinding.
>>>>
>>>> From
>>>> https://wrapt.readthedocs.io/en/latest/wrappers.html#proxy-object-attributes
>>>> :
>>>>
>>>> > Proxy Object Attributes
>>>> > When an attempt is made to access an attribute from the proxy, the
>>>> same named attribute would in normal circumstances be accessed from the
>>>> wrapped object. When updating an attributes value, or deleting the
>>>> attribute, that change will also be reflected in the wrapped object.
>>>>
>>>> From https://docs.python.org/3/library/weakref.html#weakref.proxy :
>>>>
>>>> > weakref.proxy(object[, callback])¶
>>>> > Return a proxy to object which uses a weak reference. This supports
>>>> use of the proxy in most contexts instead of requiring the explicit
>>>> dereferencing used with weak reference objects. The returned object will
>>>> have a type of either ProxyType or CallableProxyType, depending on whether
>>>> object is callable. Proxy objects are not hashable regardless of the
>>>> referent; this avoids a number of problems related to their fundamentally
>>>> mutable nature, and prevent their use as dictionary keys. callback is the
>>>> same as the parameter of the same name to the ref() function.
>>>>
>>>> On Tue, Aug 30, 2022, 1:14 AM Charles Machalow <csm10...@gmail.com>
>>>> wrote:
>>>>
>>>>> 1: There are cases where one may need the __name__/__qualname__ of a
>>>>> given callable. If someone uses partial to create a new callable, there is
>>>>> no __name__/__qualname__ given. In my particular case, I'm logging what
>>>>> callback function is passed to a different function... if someone uses
>>>>> partial, there is no __name__/__qualname__ which leads to a current
>>>>> traceback... of course i can work around it but still was an odd case to 
>>>>> me.
>>>>>
>>>>> Per the docs on functools.partial:
>>>>> "Return a new partial object which when called will behave like func
>>>>> called with the positional arguments args and keyword arguments keywords"
>>>>> ... which made me initially think that in order to behave like the
>>>>> passed in function: it should have __name__ and __qualname__... like the
>>>>> func did.
>>>>>
>>>>> 2: I would say have both __qualname__ and __name__. Both could be
>>>>> based off of __name__/__qualname__ of the passed in func.
>>>>>
>>>>> 3: This would be more difficult since you would have to
>>>>> disassemble the lambda to figure out the called method (or methods)... We
>>>>> can table the lambda discussion for the purpose of this idea. I recall 
>>>>> that
>>>>> typically it is preferred to use partial over lambdas, so this could be an
>>>>> additional functionality/benefit of using partial over lambda.
>>>>>
>>>>> Notes:
>>>>> ... __name__ being something like partial(foo, "x") would be fine with
>>>>> me... I just feel as though something should be there.
>>>>>
>>>>> - Charlie Scott Machalow
>>>>>
>>>>>
>>>>> On Mon, Aug 29, 2022 at 9:56 PM Paul Bryan <pbr...@anode.ca> wrote:
>>>>>
>>>>>> +0
>>>>>>
>>>>>> Questions:
>>>>>>
>>>>>> 1. What's the use case for partial having __name__?
>>>>>> 2. Does this imply it should have __qualname__ as well?
>>>>>> 3. What name would be given to (an inherently anonymous) lambda?
>>>>>>
>>>>>> Notes:
>>>>>>
>>>>>> 1. I would prefer __name__ to be more qualifying like its repr (e.g.
>>>>>> partial(foo, "x") → "<partial foo>")
>>>>>>
>>>>>>
>>>>>> On Mon, 2022-08-29 at 21:31 -0700, Charles Machalow wrote:
>>>>>>
>>>>>> Hey folks,
>>>>>>
>>>>>> I propose adding __name__ to functools.partial.
>>>>>>
>>>>>> >>> get_name = functools.partial(input, "name: ")
>>>>>> >>> get_name()
>>>>>> name: hi
>>>>>> 'hi'
>>>>>> >>> get_name.__name__
>>>>>> Traceback (most recent call last):
>>>>>>   File "<stdin>", line 1, in <module>
>>>>>> AttributeError: 'functools.partial' object has no attribute '__name__'
>>>>>> >>> get_name.func
>>>>>> <built-in function input>
>>>>>> >>> get_name.func.__name__
>>>>>> 'input'
>>>>>>
>>>>>> We could set __name__ based off of partial.func.__name__ or we could
>>>>>> try to set it to something like 'partial calling func.__name__'
>>>>>>
>>>>>> If the callable doesn't have a name, we could fall back to a None
>>>>>> __name__ or set it to something generic.
>>>>>>
>>>>>> Even lambdas have __name__ set:
>>>>>>
>>>>>> >>> l = lambda: input('name: ')
>>>>>> >>> l.__name__
>>>>>> '<lambda>'
>>>>>>
>>>>>> This proposal makes __name__ on partial objects more useful than the
>>>>>> current behavior of __name__ on lambda objects as well. We could port 
>>>>>> over
>>>>>> similar functionality to lambda if we'd like.
>>>>>>
>>>>>> - Charlie Scott Machalow
>>>>>> _______________________________________________
>>>>>> Python-ideas mailing list -- python-ideas@python.org
>>>>>> To unsubscribe send an email to python-ideas-le...@python.org
>>>>>> https://mail.python.org/mailman3/lists/python-ideas.python.org/
>>>>>> Message archived at
>>>>>> https://mail.python.org/archives/list/python-ideas@python.org/message/WK3FO357ORPVAD3XRUBRH6IHIYSPS3G2/
>>>>>> Code of Conduct: http://python.org/psf/codeofconduct/
>>>>>>
>>>>>>
>>>>>> _______________________________________________
>>>>> Python-ideas mailing list -- python-ideas@python.org
>>>>> To unsubscribe send an email to python-ideas-le...@python.org
>>>>> https://mail.python.org/mailman3/lists/python-ideas.python.org/
>>>>> Message archived at
>>>>> https://mail.python.org/archives/list/python-ideas@python.org/message/ONAEBCIEJ4DJNWUNWE2ESJ6SBB4O7O6W/
>>>>> Code of Conduct: http://python.org/psf/codeofconduct/
>>>>>
>>>> _______________________________________________
>> Python-ideas mailing list -- python-ideas@python.org
>> To unsubscribe send an email to python-ideas-le...@python.org
>> https://mail.python.org/mailman3/lists/python-ideas.python.org/
>> Message archived at
>> https://mail.python.org/archives/list/python-ideas@python.org/message/YGARINTBW6W32V7YELNEX4VPD7YMPFMR/
>> Code of Conduct: http://python.org/psf/codeofconduct/
>>
>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/EVEWRJZIKNOT5J3C7SWOSH5KX3CHC4IR/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to