python中__call()__函数和TensorFlow中call()函数的区别

在使用python的时候,经常用到python中的特殊函数:__call()__函数,但是学习TensorFlow的时候,又会看到自定义模型的时候,经常用call()函数,不懂他们之间的区别。

python中的__call()__函数和TensorFlow中的call()函数是两个完全不同的函数,但是又有一定的联系。

1.python中的__call__()函数:

该方法是对基类的重载函数,使得类实例对象可以像调用普通函数那样,以“对象名()”的形式使用。

比如:

class CLanguage:
    # 定义__call__方法
    def __call__(self,name,add):
        print("调用__call__()方法",name,add)

clangs = CLanguage()
clangs("C语言中文网","http://c.biancheng.net")

程序执行结果为:

调用__call__()方法 C语言中文网 http://c.biancheng.net

可以看到,通过在 CLanguage 类中实现 __call__() 方法,使的 clangs 实例对象变为了可调用对象。Python 中,凡是可以将 () 直接应用到自身并执行,都称为可调用对象。可调用对象包括自定义的函数、Python 内置函数以及本节所讲的类实例对象。
对于可调用对象,实际上“名称()”可以理解为是“名称.__call__()”的简写。仍以上面程序中定义的 clangs 实例对象为例,其最后一行代码还可以改写为如下形式:

clangs.__call__("C语言中文网","http://c.biancheng.net")

运行程序会发现,其运行结果和之前完全相同。

def say():
    print("Python教程:http://c.biancheng.net/python")
say()
say.__call__()

程序执行结果为:

Python教程:http://c.biancheng.net/python
Python教程:http://c.biancheng.net/python

用 __call__() 弥补 hasattr() 函数的短板:前面章节介绍了 hasattr() 函数的用法,该函数的功能是查找类的实例对象中是否包含指定名称的属性或者方法,但该函数有一个缺陷,即它无法判断该指定的名称,到底是类属性还是类方法。要解决这个问题,我们可以借助可调用对象的概念。要知道,类实例对象包含的方法,其实也属于可调用对象,但类属性却不是。举个例子:

class CLanguage:
    def __init__ (self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    def say(self):
        print("我正在学Python")

clangs = CLanguage()
if hasattr(clangs,"name"):
    print(hasattr(clangs.name,"__call__"))
print("**********")
if hasattr(clangs,"say"):
    print(hasattr(clangs.say,"__call__"))

程序执行结果为:

False
**********
True

可以看到,由于 name 是类属性,它没有以 __call__ 为名的 __call__() 方法;而 say 是类方法,它是可调用对象,因此它有 __call__() 方法。

关于python中特殊函数的使用,可以看http://c.biancheng.net/view/2380.html中对部分特殊函数的详细介绍。

2.TensorFlow中call()

利用网络层类对象进行前向计算时,只需要调用类的 __call__ 方法即可,即写成 model(x) 方式便可,它会自动调用类的 __call__ 方法,在 __call__ 方法中会自动调用 call()方法,这一 设定由 TensorFlow 框架自动完成,因此用户只需要将网络层的 前向计算逻辑实现在 call() 方 法 中即可。
 
class Network(keras.Model):
     # 回归网络模型
     def __init__(self):
         super(Network, self).__init__()
         # 创建 3 个全连接层
         self.fc1 = layers.Dense(64, activation='relu')
         self.fc2 = layers.Dense(64, activation='relu')
         self.fc3 = layers.Dense(1)

     def call(self, inputs, training=None, mask=None):
         # 依次通过 3 个全连接层
         x = self.fc1(inputs)
         x = self.fc2(x)
         x = self.fc3(x)
         return x


model = Network() # 创建网络类实例
# 通过 build 函数完成内部张量的创建,其中 4 为任意设置的 batch 数量,9 为输入特征长度
model.build(input_shape=(4, 9))
model.summary() # 打印网络信息
optimizer = tf.keras.optimizers.RMSprop(0.001) # 创建优化器,指定学习率
接下来实现网络训练部分。通过 Epoch 和 Step 组成的双层循环训练网络,共训练 200
个 Epoch,代码如下:
for epoch in range(200): # 200 个 Epoch
    for step, (x,y) in enumerate(train_db): # 遍历一次训练集
         # 梯度记录器,训练时需要使用它
         with tf.GradientTape() as tape:
             out = model(x) # 通过网络获得输出
             loss = tf.reduce_mean(losses.MSE(y, out)) # 计算 MSE
             mae_loss = tf.reduce_mean(losses.MAE(y, out)) # 计算 MAE
         if step % 10 == 0: # 间隔性地打印训练误差
             print(epoch, step, float(loss))
         # 计算梯度,并更新
         grads = tape.gradient(loss, model.trainable_variables)
         optimizer.apply_gradients(zip(grads, model.trainable_variables))
  • 12
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值