Какова цель атрибута __func__ в каталогах методов экземпляра в Python? - PullRequest
1 голос
/ 27 марта 2020

Когда создается экземпляр класса, для каждой функции в определении класса экземпляр будет иметь в своем каталоге атрибут с тем же именем, что и у функции. Этот «атрибут функции», предполагая, что функция является либо методом класса, либо методом экземпляра / привязки, будет иметь в качестве значения другой каталог, который включает все атрибуты, найденные в объектах функции, в дополнение к __func__ и __self__. __func__ содержит еще один каталог - каталог функции, найденный в самом классе - который, конечно, содержит все атрибуты, найденные в объектах функции (например, все атрибуты в __func__ указывают на тот же объект, что и атрибуты с одинаковыми именами в класс 'функциональный каталог). Предположительно, атрибуты в каталоге функций экземпляра отождествляются с идентичным атрибутом, найденным в каталоге __func__, когда функция вызывается из экземпляра (и когда функция вызывается из экземпляра, атрибут __call__ метода экземпляра откладывает до __func__.__call__). Почему же тогда экземпляры создают этот атрибут __func__? Почему бы не сделать так, чтобы атрибуты каталога функции непосредственного экземпляра указывали прямо на атрибуты каталога функции класса так же, как работают методы stati c? Кажется, что экземпляры используют больше памяти, чем им нужно.

Чтобы сделать это несоответствие более ясным:

class Solution:
    def maxArea(self, height):
        pass

    @staticmethod
    def i_am_static():
        pass

    @classmethod
    def i_am_a_class_method():
        pass

s = Solution()

print("\n\nDirectory of Instance Method:")
print(dir(s.maxArea))

print("\n\nDirectory of Instance Method __func__ attribute:")
print(dir(s.maxArea.__func__))

print("\n\nEqualities:")
print("s.maxArea.__func__.__call__== Solution.maxArea.__call__?  ",
s.maxArea.__func__.__call__ == Solution.maxArea.__call__)
print("s.maxArea.__call__ == Solution.maxArea.__call__?  ",
s.maxArea.__call__ == Solution.maxArea.__call__)



print("\n\nDirectory of Static Method:")
print(dir(s.i_am_static))
print("\nInstance Method has these other methods:")
for i in dir(s.maxArea):
    if i not in dir(s.i_am_static):
        print(i)
print("\nStatic Method has these other methods:")
for i in dir(s.i_am_static):
    if i not in dir(s.maxArea):
        print(i)

Отпечатки:

Directory of Instance Method:
['__call__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__func__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']


Directory of Instance Method __func__ attribute:
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']


Equalities:
s.maxArea.__func__.__call__ == Solution.maxArea.__call__?   True
s.maxArea.__call__ == Solution.maxArea.__call__?   False


Directory of Static Method:
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

Instance Method has these other methods:
__func__
__self__

Static Method has these other methods:
__annotations__
__closure__
__code__
__defaults__
__dict__
__globals__
__kwdefaults__
__module__
__name__
__qualname__

Глядя на равенства, мы можем видеть, что атрибуты в __func__ ссылаются на тот же объект, созданный самим классом. Однако атрибуты, отсутствующие в __func__ (например, __call__), не ссылаются на один и тот же объект, но, по-видимому, они вызывают этот объект. Почему go из-за проблем с воссозданием этих атрибутов за пределами __func__ просто для вызова их в __func__? Почему бы не сбросить атрибуты в __func__ в dir (s.maxArea), а атрибут __self__ express сообщит, что метод должен быть вызван как метод экземпляра?

1 Ответ

0 голосов
/ 27 марта 2020

Используется для хранения места, где находится функция в памяти.

См .: https://github.com/python/cpython/blob/24bba8cf5b8db25c19bcd1d94e8e356874d1c723/Objects/funcobject.c

Пример класса:

>>> class Test(object):
...     a = 'world'
...     def test_method(self, b='hello'):
...             print(b, self.a)
... 

Рабочий пример:

>>> c = Test()
>>> c.test_method()
hello world

Существует код, связанный с настройкой, различными внутренними переменными, аргументами, аргументами с ключевыми словами (__defaults__, __kwdefaults__ и т. Д. c), поэтому при вызове метода у него есть информация о том, как его назвать. (см. bound метод пример, также обратите внимание, что здесь есть предостережения для python2 / 3, см. связанное объяснение method-wrapper ниже)

Пример kwarg по умолчанию b:

>>> print(c.test_method.__defaults__)
('hello',)

Где расположены Test и Test.test_method:

>>> print(c)
<__main__.Test object at 0x7fd274115550>
>>> print(c.test_method)
<bound method Test.test_method of <__main__.Test object at 0x7fd274115550>>

Где находится __func__ расположен:

>>> print(c.test_method.__func__)
<function Test.test_method at 0x7fd274113598>

>>> c.test_method.__func__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: test_method() missing 1 required positional argument: 'self'

Как выглядит Test.test_method, когда он вызывается, более или менее он выглядит так:

>>> c.test_method.__func__(c)
hello world

Как насчет __call__? Это method-wrapper для __func__:

>>> print(c.test_method.__func__.__call__)
<method-wrapper '__call__' of function object at 0x7fd274113598>

>>> c.test_method.__func__.__call__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: test_method() missing 1 required positional argument: 'self'
>>> c.test_method.__func__.__call__(c)
hello world

Таким образом, цель __func__ состоит в том, чтобы настроить, как модули и классы будут искать функцию, если это метод внутри класса, у него есть некоторые дополнительные вещи, работающие за кулисами для установки контекста, чтобы он мог доступ self.a или другие методы класса внутри класса.

Пример, где функция находится за пределами класса:

>>> def test_method(cls, b='hello'):
...     print(b, cls.a)
... 

>>> class Test(object):
...     a = 'world'
... 

>>> c = Test()
>>> test_method(c)
hello world

>>> print(test_method)
<function test_method at 0x7fd274113400>

>>> print(test_method.__func__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute '__func__'

>>> print(test_method.__defaults__)
('hello',)

>>> test_method.__call__(c)
hello world

См. также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...