Ответ 1
При доступе к Foo.f
или Foo().f
возвращается метод; он несвязан в первом случае и связан со вторым. Метод python по существу является оберткой вокруг функции, которая также содержит ссылку на класс, которым является метод. При привязке он также содержит ссылку на экземпляр.
Когда вы вызываете метод, он проверит проверку первого переданного аргумента, чтобы убедиться, что это экземпляр (он должен быть экземпляром ссылочного класса или подкласса этого класса). Когда метод привязан, он предоставит этот первый аргумент по несвязанному методу, который вы предоставите ему сами.
Этот объект метода имеет атрибут __func__
, который является только ссылкой на обернутую функцию. Получая доступ к базовой функции вместо вызова метода, вы удаляете typecheck, и вы можете передать все, что хотите, в качестве первого аргумента. Функции не заботятся о своих типах аргументов, но методы делают.
Обратите внимание, что в Python 3 это изменилось; Foo.f
просто возвращает функцию, а не несвязанный метод. Foo().f
возвращает метод все еще, все еще привязанный, но больше не существует способа для создания несвязанного метода.
Под капотом каждый функциональный объект имеет __get__
method, это возвращает объект метода:
>>> class Foo(object):
... def f(self): pass
...
>>> Foo.f
<unbound method Foo.f>
>>> Foo().f
<bound method Foo.f of <__main__.Foo object at 0x11046bc10>>
>>> Foo.__dict__['f']
<function f at 0x110450230>
>>> Foo.f.__func__
<function f at 0x110450230>
>>> Foo.f.__func__.__get__(Foo(), Foo)
<bound method Foo.f of <__main__.Foo object at 0x11046bc50>>
>>> Foo.f.__func__.__get__(None, Foo)
<unbound method Foo.f>
Это не самая эффективная кодировка, поэтому в Python 3.7 добавлена новая пара опкод LOAD_METHOD
- CALL_METHOD
, которая заменяет текущую пару кода LOAD_ATTRIBUTE
- CALL_FUNCTION
точно, чтобы избежать создания нового объекта метода каждый время. Эта оптимизация преобразует путь выполнения для instance.foo()
из type(instance).__dict__['foo'].__get__(instance, type(instance))()
с помощью type(instance).__dict__['foo'](instance)
, поэтому "вручную" передается в экземпляр непосредственно объекту функции. Это экономит около 20% времени на существующих микрообъектах.