【问题标题】:What is the purpose of the __func__ attribute in instance method directories in Python?Python 实例方法目录中的 __func__ 属性的用途是什么?
【发布时间】:2020-03-27 19:26:39
【问题描述】:

当一个类的实例被创建时,对于类定义中的每个函数,该实例的目录中都会有一个与函数同名的属性。这个“函数属性”,假设函数是类方法或实例/绑定方法,将有另一个目录作为其值,其中包括除__func____self__ 之外的函数对象中的所有属性。 __func__ 包含另一个目录——在类本身中找到的函数的目录——它当然包含在函数对象中找到的所有属性(例如,__func__ 中的所有属性与类的函数目录)。据推测,当从实例调用函数时,实例函数目录中的属性会延迟到在 __func__ 目录中找到的相同属性(并且当从实例调用函数时,实例的方法的 __call__ 属性会延迟到__func__.__call__)。那么为什么实例仍然创建这个__func__ 属性呢?为什么不让直接实例函数目录属性直接指向类函数目录属性,就像静态方法操作一样?似乎实例使用的内存超出了它们的需要。

为了更清楚地说明这种差异:

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__)没有引用同一个对象,但它们可能调用了那个对象。为什么要麻烦在__func__ 之外重新创建这些属性,只是为了调用__func__ 中的那些?为什么不将__func__ 中的属性转储到dir(s.maxArea) 中,__self__ 属性会表示该方法应该作为实例方法调用?

【问题讨论】:

    标签: python class directory attributes instance


    【解决方案1】:

    用于保存函数在内存中的位置。

    见: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__ 等)相关的代码,因此当调用该方法时,它具有有关如何调用它的信息。 (参见bound method 示例,另请注意这里有python2/3 的警告,请参见下面链接的method-wrapper 解释)

    默认 kwarg 示例b:

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

    TestTest.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
    
    

    另见:

    【讨论】:

    • 您的回答包含了很好的信息,帮助我更好地理解了类方法。但是,我认为我的主要问题仍未得到解答。我知道__func__ 用于保存方法的目录,以帮助设置调用的上下文。但是,如果实例方法目录所做的只是委托给__func__ 中的方法,为什么还要重新定义与__func__ 中完全相同的属性呢?静态方法没有定义__func__,因为它们只是浅拷贝原始字典。为什么实例和类方法做的不一样,只包含__self__
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    相关资源
    最近更新 更多