14

What arguments do types.MethodType expect and what does it return? https://docs.python.org/3.6/library/types.html doesn't say more about it:

types.MethodType

The type of methods of user-defined class instances.

For an example, from https://docs.python.org/3.6/howto/descriptor.html

To support method calls, functions include the __get__() method for binding methods during attribute access. This means that all functions are non-data descriptors which return bound or unbound methods depending whether they are invoked from an object or a class. In pure python, it works like this:

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        if obj is None:
            return self
        return types.MethodType(self, obj)
  • Must the first argument self of types.MethodType be a callable object? In other words, must the class Function be a callable type, i.e. must Function have a method __call__?

  • If self is a callable object, does it take at least one argument?

  • Does types.MethodType(self, obj) mean giving obj as the first argument to the callable object self, i.e. currying self with obj?

  • How does types.MethodType(self, obj) create and return an instance of types.MethodType?

Thanks.

  • i came here by way googling it up from a StratelyPattern example @ https://github.com/faif/python-patterns/blob/master/behavioral/strategy.py so it seems it can do stuff past type checking. looks like it can be used for binding a function to transform it to be a method. not totally sure though. – JL Peyret Nov 25 '18 at 00:01
  • Trying to find out how to bind a function as a `classmethod` to an `enum.Enum`. Seems like this level of managing Python is just not documented. My use-case doesn't seem so alien either. I'm just trying to implement Enum validation by name instead of value for Pydantic. It's not there so I have to put it in and it feels like it's impossible. Would love to know where this is documented, even if I have to buy a book. – NeilG Mar 02 '23 at 01:14

4 Answers4

7

Usually you don't need to create instance of types.MethodType yourself. Instead, you'll get one automatically when you access a method on an instance of a class.

For example, if we make a class, create an instance of it, then access a method on the instance (without calling it), we'll get an instance of types.MethodType back:

import types

class Foo:
    def bar(self):
        pass

foo = Foo()

method = foo.bar

print(type(method) == types.MethodType) # prints True

The code you excerpt in your question is trying to show how this normally happens. It's not something you usually have to do yourself, though you can if you really want to. For instance, to create another instance of types.MethodType equivalent to method above, we could do:

method_manual = types.MethodType(Foo.bar, foo)

The first argument to MethodType is a callable object (normally a function, but it can be something else, like an instance of the Function class in the example you were reading). The second argument what we're binding the function to. When you call the method object (with e.g. method()), the bound object will be passed into the function as the first argument.

Usually the object the method gets bound to is an instance, though it can be something else. For instance, a classmethod decorated function will bind to the class it is called on, rather than an instance. Here's an example of that (both getting a method bound to a class automatically, and doing it manually ourselves):

class Foo2:
    @classmethod
    def baz(cls):
        pass

foo2 = Foo2()

method2 = Foo2.baz
method2_via_an_instance = foo2.baz
method2_manual = types.MethodType(method2.__func__, Foo2)

All three of the method2-prefixed variables work exactly the same way (when you call them, they'll all call baz with Foo2 as the cls argument). The only wonky thing about the manual approach this time is that it's hard to get at the original baz function without getting a bound method instead, so I fished it out of one of the other bound method objects.

A final note: The name types.MethodType is an alias for the internal type used for bound methods, which doesn't otherwise have an accessible name. Unlike many classes, the repr of an instance is not an expression to recreate it (it will be something like "<bound method Foo.bar of <__main__.Foo object at 0x0000...>>"). Nor is the repr of the type a valid name to access the type by (the repr is "method").

Blckknght
  • 100,903
  • 11
  • 120
  • 169
3

Short Answer:

Must the first argument self of types.MethodType be a callable object? In other words, must the class Function be a callable type, i.e. must Function have a method __call__?

Yes

If self is a callable object, does it take at least one argument?

Depends

Does types.MethodType(self, obj) mean giving obj as the first argument to the callable object self, i.e. currying self with obj?

Yes

How does types.MethodType(self, obj) create and return an instance of types.MethodType?

It doesn't work like that.


Long Answer:

the code

class Function(object):
    . . .
    def __get__(self, obj, objtype=None):
        "Simulate func_descr_get() in Objects/funcobject.c"
        if obj is None:
            return self
        return types.MethodType(self, obj)

As Daniel explained is mainly to demonstrate for

To support method calls, functions include the __get__() method for binding methods during attribute access. This means that all functions are non-data descriptors which return bound or unbound methods depending whether they are invoked from an object or a class. In pure python, it works like this:

The types.MethodType() works when the Function has an object.
if obj is None would be False
Then it's a method of some object aka. bound method.

It explains how Python grammar work. As a function, it could be called in the following two ways.

some_func_() or some_class.some_func()

The former part https://docs.python.org/3.6/howto/descriptor.html#invoking-descriptors explained.

For objects, the machinery is in object.__getattribute__() which transforms b.x into type(b).__dict__['x'].__get__(b, type(b)). The implementation works through a precedence chain that gives data descriptors priority over instance variables, instance variables priority over non-data descriptors, and assigns lowest priority to __getattr__() if provided.

Here it's some demonstrate code

>>> import types
>>> types.MethodType
<type 'instancemethod'>
>>> def a(self):
...     print(1)
... 
>>> class B:
...     pass
... 
>>> types.MethodType(a,B)
<bound method ?.a of <class __main__.B at 0x7f4d3d5aa598>>
>>> B.t = types.MethodType(a,B)
>>> B.t()
1
>>> def s():
...     print(3)
... 
>>> B.r = types.MethodType(s,B)
>>> B.r
<bound method ?.s of <class __main__.B at 0x7f4d3d5aa598>>
>>> B.r()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: s() takes no arguments (1 given)

See also dynamically adding callable to class as instance "method"

Shihe Zhang
  • 2,641
  • 5
  • 36
  • 57
  • I think your last two "shorter" answers are just plain wrong. Binding a method is indeed similar to currying, and `types.MethodType(self, obj)` creates a bound method because you're calling a class with arguments, just like `list(something)` creates a list, and `str(something)` creates a string. – Blckknght Mar 16 '19 at 06:32
  • Thank you for the comment. After reviewed the question, I found I miss the `ie. curring ...` part. I would update that part. If you agree with `types.MethodType(self, obj) create and return an instance of types.MethodType` would you like to provide some demonstrate code? – Shihe Zhang Mar 18 '19 at 02:24
  • 1
    Um, I'm not sure how to provide an example of something that *is already an example*, that works if you run it (the `Function` class in the linked documentation is intended as a demonstration of how normal functions behave as descriptors, but it *does* work). If you want something simpler, try `m = types.MethodType(lambda self: print(self), [1, 2, 3])`. The variable `m` will be assigned a callable object that prints the list `[1, 2, 3]` when called with no objects. The type of `m` is `types.MethodType`. – Blckknght Mar 18 '19 at 02:36
  • Sorry, there are both Python 2 and Python 3 here. Used Python 2 by mistake. `>>> m = types.MethodType(lambda self: print(self), [1,2,3]) >>> type(m) >>> m of [1, 2, 3]> ` this is quite illuminating. – Shihe Zhang Mar 18 '19 at 03:26
2

Documentation doesn't say much, but you can always check its source code. The signature of MethodType constructor is:

def __init__(self, func: Callable[..., Any], obj: object) -> None: ...

It accepts a callable and object, and returns None.

MethodType can be used to add an instance method to an object, instead of a function; here's an example:

from types import MethodType


class MyClass:
    language = 'Python'


def target_function(self):
    print(f'Hello {self.language}!')


# the function is not bound to the object, this is just an assignment
obj1 = MyClass()
obj1.say_hello = target_function
print('assignment:', type(obj1.say_hello))  # type is class 'function'
try:
    obj1.say_hello()
except Exception as e:
    print('exception:', str(e))

# a function is bound to obj2 and becomes a method
obj2 = MyClass()
# this is used to bind a function and make it a "method" to a specific object obj2
obj2.say_hello = MethodType(target_function, obj2)
print('MethodType:', type(obj2.say_hello))  # type is class 'method'
obj2.say_hello()

Louis Huang
  • 589
  • 3
  • 7
  • 1
    "Binding" doesn't mean what you're using it to mean. What you are calling "binding" is just assigning an attribute. "Binding" is when a function is modified to refer to the object it is bound to through it's first argument, when called. Thus when a function is "bound" to an object it becomes a method. Before it is bound it is a function. After it is bound it is a method. The `lambda` in your example is *not* a method. It's actually a lambda but you could put a function there. If you did it would still be a function, not a method, until after it's "bound". Correct terminology is important. – NeilG Mar 02 '23 at 01:35
  • Thank you, I was sloppy and didn't notice I used the wrong terminology. I updated my comments. – Louis Huang Mar 02 '23 at 12:29
  • thanks for updating that, unfortunately I've since discovered that my previous understanding of "bound" and "unbound" is now wrong, too, at least for Python 3. I now discover that there are "functions" that are used by an object but are still not called "methods", there are "unbound methods" and there are "bound methods", and also "binding" whatever that means now, can also happen when the function (or method, who knows anymore) is called, and not when it's assigned. And I'm a bit frustrated with the Python docs by now. You can call them what you like, IDC anymore! – NeilG Mar 03 '23 at 02:10
  • LOL! Okay, but I get what you mean. Let just use "bind" in the traditional sense of OOP. – Louis Huang Mar 03 '23 at 06:38
-1

It's not something you would ever call. Like most of the classes in the types module, it's more for comparing with existing objects (for example in isinstance).

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Thanks. How do you understand the arguments of `types.MethodType(self, obj)` and what it does? –  Oct 02 '17 at 12:11
  • 1
    I don't understand your question. MethodType is a class. That code is passing `self` and `obj` from the Function `__get__` method. But note, again, that is not real code; as the comment says, it's just simulating what the underlying C implementation does to create a method. – Daniel Roseman Oct 02 '17 at 12:12
  • I just tried to edit my questions to make them clearer. –  Oct 02 '17 at 13:28
  • Well unfortunately they're still unanswerable. "Does `self` take an argument" - well it depends on the function. "How does it create an instance" - just like any other class. But once again, this code is *imaginary* and not something that you would ever run. – Daniel Roseman Oct 02 '17 at 13:34
  • `MethodType` actually does have usage cases aside from comparison. For example, dynamic classmethod generation with instance binding. See: https://stackoverflow.com/questions/14526652/dynamically-adding-callable-to-class-as-instance-method – Enteleform Jul 23 '18 at 23:21