title: Create standalone functions from class methods ("uncurry"?)
body: <p>I have a class with many methods, which I'd like to be able to call as 'standalone' functions as well.</p>
  <p>An example to clarify:</p>
  <h3>Starting point</h3>
  <p>A class <code>Person</code>, and a dictionary of <code>Person</code> instances. The class methods can be called on each of the instances, as seen in the last two lines.</p>
  <pre><code>from typing import Callable
  
  
  class Person:
      def __init__(self, age: int, profession: str):
          self._age = age
          self._pro = profession
  
      def describe(self) -&gt; str:
          &quot;&quot;&quot;Describes the person in a sentance.
  
          Parameters
          ----------
          None
  
          Returns
          -------
          str
          &quot;&quot;&quot;
          return f&quot;This person is {self._age} years old and is a {self._pro}.&quot;
  
      def getolder(self, years: int = 1) -&gt; None:
          &quot;&quot;&quot;Increases the age of the person.
  
          Parameters
          ----------
          years : int, optional
              Number of years to increase age by, by default 1
  
          Returns
          -------
          None
          &quot;&quot;&quot;
          self._age += years
  
      # (and many more)
  
  
  colleagues = {
      &quot;john&quot;: Person(39, &quot;accountant&quot;),
      &quot;jill&quot;: Person(40, &quot;boss&quot;),
      &quot;jack&quot;: Person(25, &quot;intern&quot;),
  }
  
  colleagues[&quot;john&quot;].describe() #'This person is 39 years old and is a accountant.'
  colleagues[&quot;john&quot;].getolder(4)
  </code></pre>
  <h3>Goal and current solution</h3>
  <p>Now, I'd like to abstract the dictionary away, and create functions <code>describe</code> and <code>getolder</code> in such a way, that I can call <code>describe('john')</code> and <code>getolder('john', 4)</code> instead.</p>
  <p>I use the following function, which works:</p>
  <pre><code>def uncurry(original: Callable) -&gt; Callable:
      def uncurried(name, *args, **kwargs):
          return original(colleagues[name], *args, **kwargs)
  
      # Add parameter to docstring.
      doc = original.__doc__
      search = &quot;Parameters\n        ----------\n&quot;
      insert = &quot;        name : str\n            Name of the person.\n&quot;
      pos = doc.find(search)
      if pos == -1:
          raise ValueError(&quot;Unexpected docstring format.&quot;)
      pos += len(search)
      uncurried.__doc__ = doc[:pos] + insert + doc[pos:]
  
      return uncurried
  
  describe = uncurry(Person.describe)
  getolder = uncurry(Person.getolder)
  
  describe(&quot;john&quot;) #'This person is 43 years old and is a accountant.'
  getolder(&quot;john&quot;, 4)
  </code></pre>
  <h3>Issues</h3>
  <p>The current solution has the following issues, see below.</p>
  <ul>
  <li><p>The signatures of the <code>describe</code> and <code>getolder</code> functions do not show the parameters.</p>
  </li>
  <li><p>The original docstring is missing the <code>name</code> parameter, which I add in a rather hacky way, and which also is not perfect (e.g., if the original function has no parameters).</p>
  </li>
  </ul>
  <p>If there is a better solution, I'd love to hear it.</p>
  <pre><code>&gt;&gt;&gt; help(describe)
  
  Signature: describe(name, *args, **kwargs)
  Docstring:
  Describes the person in a sentance.
  
  Parameters
  ----------
  name : str
      Name of the person.
  None
  
  Returns
  -------
  str
  File:      c:\users\ruud.wijtvliet\ruud\python\dev\belvys\belvys\&lt;ipython-input-24-bbc7c1f4a60a&gt;
  Type:      function
  </code></pre>