Functions, Callable Objects, and how both are created in Python

后端 未结 2 1084
攒了一身酷
攒了一身酷 2020-12-15 06:04

I\'m wondering about the more intricate differences between functions and callable objects. For example, if you do:

def foo1():
   return 2 + 4

class foo2:
         


        
相关标签:
2条回答
  • 2020-12-15 06:50

    Functions are also callable objects:

    >>> foo1.__call__
    <method-wrapper '__call__' of function object at 0x105bafd90>
    >>> callable(foo1)
    True
    

    But a class needs to keep track of more information; it doesn't matter here that you gave it a __call__ method. Any class is bigger than a function:

    >>> import sys
    >>> def foo1():
    ...    return 2 + 4
    ...
    >>> class foo3:
    ...    pass
    ...
    >>> sys.getsizeof(foo1)
    136
    >>> sys.getsizeof(foo3)
    1056
    

    A function object is a distinct object type:

    >>> type(foo1)
    <class 'function'>
    

    and is reasonably compact because the meat is not actually in the function object but in other objects referenced by the function object:

    >>> sys.getsizeof(foo1.__code__)
    144
    >>> sys.getsizeof(foo1.__dict__)
    240
    

    And that's it really; different types of objects have different sizes because they track different things or use composition to store stuff in other objects.

    You can use the type(foo1) return value (or types.FunctionType, which is the same object) to produce new function objects if you so desire:

    >>> import types
    >>> types.FunctionType(foo1.__code__, globals(), 'somename')
    <function foo1 at 0x105fbc510>
    

    which is basically what the interpreter does whenever a def function(..): ... statement is being executed.

    Use __call__ to make custom classes callable when that makes sense to your API. The enum.Enum() class is callable, for example, specifically because using the call syntax gives you a syntax distinct from subscription, which was used for other purposes. And a xmlrpc.client.ServerProxy() object produces method objects that are instances of _Method, because they proxy a remote call, not a local function.

    0 讨论(0)
  • 2020-12-15 07:02

    Is there a version of type() but for functions, a metafunction?

    Sort of. Functions have a type, and that type can be used to construct new functions from, at minimum, a code object and a globals dictionary. (The code object can be constructed using compile() or grabbed from an existing function, or, if you're a masochist, built from a bytecode string and other information using the code type's constructor.) The function type is not a meta-anything because functions are instances, not classes. You can get this type using type(lambda:0) (putting any function in the parentheses), and do help(type(lambda:0)) to see its arguments. The function type is an instance of type.

    Say someone wanted to write their own metafunction

    You can't subclass the function type, sorry.

    class FunkyFunc(type(lambda: 0)): pass
    
    TypeError: Error when calling the metaclass bases
        type 'function' is not an acceptable base type
    

    Is the only purpose behind having a callable object to have a function that can store info in it as well?

    There are many uses for it. Your example is one (although function instances can have attributes, a class provides better documentation of the attributes); the flip side, making a plain old data object callable, is another (I gave a kind of funky example of that in this answer). You can use classes to write decorators if the instances are callable. You can use __call__ on a metaclass to customize instance construction. And you can use it to write functions with different behavior, sort of as if you could in fact subclass the function type. (If you want C-style "static" variables in a function, you might write it as a class with a __call__ method and use attributes to store the static data.)

    A fun thing about function objects is that, because they're callable, they themselves have a __call__ method. But a method is callable, and so that __call__ method also has a __call__ method, and that __call__ method (being callable) also has a __call__ method, and so on ad infinitum. :-)

    0 讨论(0)
提交回复
热议问题