python中class使用总结
一 内部调用和__call__()
详细说明参考:https://blog.csdn.net/weixin_43593330/article/details/108174666
注意:双前导和双末尾下划线__var__在python中用于特殊用途,具体参考:https://www.runoob.com/w3cnote/python-5-underline.html,但是不是所有的此类都可以使得一个对象可以被调用,python中默认实现了__call__,pytorch的torch.utils.data.Dataset 中实现了__getitem__,keras的keras.utils.Sequence中实现了__getitem__。
Python 中下划线的 5 种含义:单前导下划线_var,单末尾下划线var_,双前导下划线__var,双前导和末尾下划线__var__,单下划线_ 参考:https://www.runoob.com/w3cnote/python-5-underline.html。
Python中的双下划线__xxx__(魔术方法) 参考:https://www.cnblogs.com/bigtreei/p/7930511.html
call()是一种magic method,在类中实现这一方法可以使该类的实例(对象)像函数一样被调用。默认情况下该方法在类中是没有被实现的。
call()方法的作用其实是把一个类的实例化对象变成了可调用对象,只要类里实现了__call__()方法就行。如当类里没有实现__call__()时,此时的对象p 只是个类的实例,不是一个可调用的对象,当调用它时会报错:‘Person’ object is not callable.
class People(object):
def __init__(self, name):
self.name = name
def my_friend(self):
print("hello "+self.name)
def train(self,location):
print("name"+location)
def __call__(self):
return self.my_friend()
# return self.train(location)
def update(self, newname):
self.name = newname
a = People('无忌!')
a.__call__() # 调用方法一
a() # 调用方法二 调用就会直接激活__call__(),调用时必须加上括号
# 调用类中其他对象
a.update("赵敏") # 更新类中变量,更新了类中的全局变量,调用时a后面不加括号
a()
class People(object):
def __init__(self, name):
self.name = name
def add_interest(self, interest): # 定义类中新的全局变量,也就是在类中的初始化函数中没有定义的变量。
# 不能直接给定,必须通过这种函数方法来传入
self.interest = interest
def change_interest(self): # 前面定义好新的变量后,在这里更改
self.interest = "play guitar"
def speak(self): # 不需要传入参数的对象
print("come here please")
def train(self, location): # 初始化时传入的参数这里不能用,需要在这个对象中传入参数,只对这个函数起作用
print("name"+" "+location)
def my_friend(self, location): # 调用类中的其他对象。如果被调用函数需要参数,则传入参数
self.change_interest() # 不需要参数的调用
self.speak() # 不需要参数的调用
self.train(location) # 需要参数的调用
print("hello "+" "+self.name)
print("I like"+" "+self.interest)
def __call__(self, location): # __call__()方法的作用其实是把一个类的实例化对象变成了可调用对象,
# 只要类里实现了__call__()方法就行,即实例化类后,直接调用类就会调用__call__
return self.my_friend(location)
# return self.train(location)
def update(self, newname): # 更新类中全局变量的函数
self.name = newname
def play(self, ball): # 跟类中其他对象没有关系的独立对象
print("play"+ball)
a = People('无忌!')
a.add_interest("sing song")
a.__call__("北京") # 调用方法一
print("---------------------")
a("北京") # 调用方法二
print("---------------------")
# 调用类中其他对象
a.update("赵敏") # 更新类中变量
a("北京")
print("---------------------")
# 调用类中独立对象
a.play("football")
print("---------------------")
# 打印类中的全局变量
print(a.name)
print(a.interest)
二 在循环遍历中更新类中的全局变量
如果想要在循环中自动更新类中的全局变量,则必须将类的初始化写到循环外面。
# 定义一个类
class step_batch():
def __init__(self, lr=10):
self.lr = lr
def run(self):
print(self.lr)
def update(self):
self.lr = self.lr / 2
# 类的初始化在循环里面,意味着每次循环都需要重新初始化,那么循环中的更新变量的操作就会被类的重新初始化给更改掉
def train_model(init_lr=10):
for i in tqdm(range(10)):
a = step_batch(lr=init_lr) # 类的初始化
a.run()
if i % 2 == 0:
a.update()
# 类的初始化在循环外面,意味着在进入循环之前只进行了一次初始化;在循环体内部,更新变量的操作就会将更新后的变量保存下来
def test_model(init_lr=10):
a = step_batch(lr=init_lr) # 类的初始化
for i in tqdm(range(10)):
a.run()
if i % 2 == 0:
a.update()
# 结果如下
类的初始化在循环中
10
10
10
10
10
10
10
10
10
10
---------------------
类的初始化在循环外面
10
5.0
5.0
2.5
2.5
1.25
1.25
0.625
0.625
0.3125
100%|██████████| 10/10 [00:00<?, ?it/s]
100%|██████████| 10/10 [00:00<?, ?it/s]
总结:
实例化 StepRunner() 类,会自动运行 init()进行初始化,给__init__()中定义的变量和函数传参数,赋值。
注意:实例化类以后,后面就可以去调用类中的对象,更改类中的对象。调用类中的哪一个对象(函数),这个对象(函数)才会受影响,没调用就不受影响,也就是,如果调用的这个对象(函数)不影响 init() 中的变量和函数,那么__init__() 中的变量和函数就不改变。如果调用的对象(函数)和__init__() 中的内容有关系,那么__init__() 中的变量和函数就改变。
初始化时候定义的那些类中的变量,也可以更改,这样就会永久改变。更改哪一个,哪一个才受影响,不更改就不会受影响。
这里最有迷惑性的是类中的__call__(), call()的作用就是在实例化后可以通过直接调用类名来调用__call__()中定义的内容,而不需要再写class().step() 类加函数 的形式。只需要关注__call__()中定义了什么就可以。