python中__call__()深度分析

本文深入探讨Python中的__call__方法,通过实例解释其作用,如何使得类的对象可被调用。同时,文章分析了TestClass3()与test3()的区别,包括它们的类型和属性差异,并解答了为何test1.c会报错的问题。最后,介绍了内置函数dir()的多种用法。
部署运行你感兴趣的模型镜像

前言:

       在开始介绍__call__()函数时,先来看一个例子:

class TestClass(object):
    a=1
    def __init__(self):
        self.c=4

    def do_func(self):
        print("这是方法")


test=TestClass()
test1=TestClass
print(test.a)
print(test.c)
print(test1().c)
print(test1.c)

程序的运行结果:

可以思考下,为什么test1.c会报错

__call__()的介绍:

      __call__()函数其实就是一个特殊的实例方法,在使用的时候不需要像test.do_func()或者是像test1.do_func()方式调用,直接使用test()的方式就可以,可以很多的时候直接在实例化的对象后加()会报错,那是因为没有实现__call__()函数,如下图:

图中创建了一个类TestClass,再实例化了一个对象test3,让test3()这样会报错,因为TestClass2中没有实现__call__()方法,且继承的object类也没有实现该方法,所以这样使用就会报错如下:

我们加上这个方法,如下例子:

这样的话就很清楚的知道test3()其实就等同于test3.__call__().

()使用延伸:

在平时的使用的经常会遇到如,TestClass3() 和test3()这种的情况,下面我们就分析下:

1.TestCalss3():是类本省调用__new__()方法,来创建一个实例对象,写法等价于TestClass3().__new__()

2.test3():是实例化后的对象调用__call__()方法,来实现对这个方法的调用,写法等价于TestClass3().__new__().__call__()

具体的可以通过判断这两种调用后的类型和对应的属性来验证:

class TestClass(object):
    a=1
    def __init__(self):
        self.c=3


    def do_func(self):
        print("这是方法")

    def __call__(self,A,B):
        self.A=A
        self.B=B
        return  A+B

test1=TestClass
print("----type-----")
print("test1的类型:"+str(type(test1)))
print("test1()的类型:"+str(type(test1())))
print("test1.__call__()的类型:"+str(type(test1.__call__(test1,1,2))))
print("test1.__new__()的类型:"+str(type(test1.__new__(test1))))
print("--------dir-------")
print("test1的类型:"+str(dir(test1)))
print("test1()的类型:"+str(dir(test1())))
print("test1.__call__()的类型:"+str(dir(test1.__call__(test1,2,2))))
print("test1.__new__()的类型:"+str(dir(test1.__new__(test1))))

执行后的结果是:

----type-----
test1的类型:<class 'type'>
test1()的类型:<class '__main__.TestClass'>
test1.__call__()的类型:<class 'int'>
test1.__new__()的类型:<class '__main__.TestClass'>
--------dir-------
test1的类型:['A', 'B', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute_
_', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__
repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'do_func']
test1()的类型:['A', 'B', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribut
e__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '
__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'c', 'do_func']
test1.__call__()的类型:['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__',
'__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__',
 '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '
__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__
rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__size
of__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator'
, 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
test1.__new__()的类型:['A', 'B', '__call__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__g
etattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduc
e_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'do_func']

开始对type的分析:

1.test1的类型是type,因为test1=TestClass,指向的是类本身,而在python中一切皆对象(这也是python核心的思想),python中规定的一切皆对象,那么类本身也是对象,是对象就会有类型,我们一般的示例对象的类型就是累本身,但是当我们判断类本身时候就需要用一种类型来归纳python中所有的类,所以指定了type为所有类本身的类型,这样可能会有这样的疑问,从一切皆对象的思想出发,type也应该是对象,相应的也应该是有类型的,如果这样一直指定下去,python估计就不用玩了,所以type的类型就是它本身,如下验证:

2.test1()的类型是TestClass,test1()是TestClass的实例化对象,所以类型就是TestClass

3.test1().__call__(test1,1,2)的类型是int类型,type(test1().__call__(test1,1,2))等价于type(A+B),其中A、B是int类型的,所以返回的结果也是int类型

4.test1.__new__(test1))的类型也是TestClass,从这里就知道test1.__new__(test1))==TestClass(),只是后者匿名对象

下面是对属性的分析:

1.由于我们已经通过上面对不同的调用方式对应的类型做了分析,在对属性进行分析时,就不再重复的去看,重点看1、2、4这三个对象的属性差异,通过上面的截图可知,比较有疑问的地方是在__call__()方法中的A、B属性,和__init__()方法的c属性,对于类中全局变量a应该不用多说,不管是类本身还是实例化的对象都会有这个变量,重点是在A、B、c这三个属性中,分成两类来说明:

     1.1 先说A、B这两个属性,可能会有疑问,明明是在方法内部的属性,为什么类本身、实例化对象都能调用,在此需要说明下self的作用,python中self指代的本身:

         1.1.1 先分析test1是类本身,此时self.A就相当于TestClass.A,所以test1中有A、B这两个属性

         1.1.2 test1()是类的实例化对象,此时的self.A就相当于test1().A,所以也有A、B这两个属性

         1.1.3 test1.__new__(test1)其实质是等于test1()的,所以不做多的解释

    1.2 再说c属性,这里会有很大的疑问,不是说test1()等于 test1.__new__(test1),其实这里是存在问题,从类型上判断是可以等于的,但是实际python解释器在看到这两种方式的时候,会执行不同的步骤:

         1.2.1 先说test1(),python解释器在看到这个的时候会调用__new__()方法,创建一个实例对象,但是之后还用调用__init__方法对这个对象初始化一个属性c,从这里可以看出,这两个是不等价的,这也是为什么test1.__new__(test1)中没有c的原因,至于实例对象的创建相关的在新的文章中说明

问题解答:

      在文章开始说的test1.c会报错,那是因为test1指向的是TestClass类本身,而类本身有的属性包含上面全局变量、方法、及通过self.x的属性,__init__()是在实例化的时候将其中的属性初始化给对象,而类本身是没有这个属性的。

内置函数dir()的用法:

1.查看当前空间的属性和方法:dir()

2.查看具体对象的属性和方法:dir(test)

3.查看不同的数据类型的属性与方法:dir("")、dir([])、dir({})

4.查看某个模块的属性和方法,先将包导入到当前空间 在使用dir(package_name)

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

<think>好的,我需要回答用户关于Python中`_call_impl`函数的功能是什么的问题。首先,我要确定这个函数是Python内置的还是某个库中的,比如TensorFlow或PyTorch,因为这些框架有时会有内部函数使用似的名字。 用户之前的问题是关于`super().__init__()`的,现在转到`_call_impl`,这可能意味着他们在学习过程中遇到了这个函数。根据我的知识库,`_call_impl`并不是Python语言内置的标准方法或函数。Python中有一些特殊方法,如`__call__`,用于使对象可调用,但`_call_impl`可能是一个特定框架或库中的实现细节。 接下来,我需要回忆常见的Python库中是否有`_call_impl`。例如,在TensorFlow或Keras中,模型可能有这样的内部方法。比如,Keras的Layer可能有`_call_impl`作为实际执行调用的内部方法,而公开的`__call__`方法会调用它,以处理输入参数和状态管理。似的,PyTorch或其他深度学习框架也可能有这样的结构,但需要确认。 如果`_call_impl`是TensorFlow/Keras中的,那么它的作用可能是处理层的调用逻辑,比如前向传播,同时管理掩码、训练状态等。例如,当用户调用一个层实例时,`__call__`方法会做一些预处理,然后调用`_call_impl`来执行实际的计算,确保在训练和推理时的不同行为,或者处理输入参数的正确性。 另外,用户可能在使用某些高级库或自定义时遇到了这个函数,比如某些框架自动生成的方法,或者是某些元编程的结果。也有可能`_call_impl`是某个特定项目或代码库中的内部实现,用于分离接口和实现,使得公共方法`__call__`处理参数检查和预处理,而`_call_impl`处理实际逻辑。 需要注意的是,由于`_call_impl`通常以下划线开头,这表明它是内部实现的一部分,不建议用户直接调用。因此,在回答时需要强调这一点,并建议用户查阅相关框架的文档或源代码以获取准确信息,而不是直接依赖这个内部方法。 总结可能的点: 1. `_call_impl`可能出现在深度学习框架如TensorFlow/Keras中,作为层或模型调用的内部实现。 2. 它的功能是处理实际的前向传播逻辑,而公开的`__call__`方法处理输入参数、状态管理等。 3. 用户通常不需要直接调用它,因为它是框架内部使用的。 4. 需要根据上下文具体分析,建议查看相关文档或代码。 在确认这些点后,我需要以清晰的结构向用户解释,同时指出可能的框架来源,并强调内部方法的使用注意事项。</think>在 Python 中,**`_call_impl` 通常不是 Python 语言自身的标准方法**,而是某些库或框架(如 **TensorFlow/Keras**、PyTorch 等)中用于实现对象调用逻辑的内部函数。以下是具体解析: --- ### **核心功能** 1. **作为内部实现方法** `_call_impl` 常见于深度学习框架(如 Keras 的 `Layer` 或自定义模型),用于**封装实际的前向传播逻辑**。它通常被公开的 `__call__` 方法调用,负责处理输入参数、状态管理(如训练/推理模式)后,再执行核心计算。 2. **分离接口与实现** 框架通过 `__call__` 方法定义用户调用的接口(如参数检查、预处理),而将具体实现逻辑放在 `_call_impl` 中,提高代码可维护性。 --- ### **典型应用场景(以 Keras 为例)** 在 Keras 中,`Layer` 调用流程如下: ```python class Layer: def __call__(self, inputs, **kwargs): # 预处理(如输入验证、状态管理) return self._call_impl(inputs, **kwargs) def _call_impl(self, inputs, **kwargs): # 实际的前向传播计算 return self.call(inputs, **kwargs) def call(self, inputs, **kwargs): # 用户自定义的逻辑(需子重写) pass ``` - **`__call__`**:用户直接调用的方法,处理通用逻辑(如输入验证、训练模式标记)。 - **`_call_impl`**:内部中转方法,可能处理框架级操作(如自动适配不同输入型)。 - **`call`**:用户需重写的方法,定义实际计算逻辑。 --- ### **为什么需要 `_call_impl`?** 1. **统一入口** 框架通过 `__call__` 集中处理公共逻辑(如参数检查、自动构建层),再通过 `_call_impl` 调用具体实现,避免重复代码。 2. **支持扩展** 允许框架在 `_call_impl` 中插入额外功能(如动态图/静态图切换),而用户无需感知。 3. **状态管理** 例如,在 `_call_impl` 中处理 `training` 标志(区分训练和推理模式),影响层的行为(如 Dropout 是否生效)。 --- ### **注意事项** 1. **避免直接调用** `_call_impl` 是框架内部方法(通常以单下划线 `_` 开头),**不建议用户直接使用**。应通过公开接口(如 `__call__` 或 `call()`)操作。 2. **框架依赖性** 不同框架的实现可能不同。例如: - **TensorFlow/Keras**:`_call_impl` 用于处理层的调用流程。 - **自定义**:某些代码可能模仿此模式,但需结合具体上下文分析。 --- ### **总结** `_call_impl` 是深度学习框架中常见的内部方法,用于分离调用接口与实际实现,确保代码的模块化和可维护性。对于普通 Python 开发,通常无需关注它;但在使用 TensorFlow/Keras 等框架时,理解其角色有助于深入调试和自定义模型。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值