简介 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的。
面向对象技术简介
何时使用类
数据与操作紧密相关
对象有许多状态需要维护,可以使用类中的属性来保存状态。
需要生成多个仅在部分属性不同的实例,可以使用类作为模板。
不同对象存在公共parent-child的层次关系,可以使用继承来复用代码。
隐藏对象的实现细节,只对外公开接口。
类变量 与 实例变量 在Python中,类变量和实例变量是两个不同的概念:
类变量(Class Variable)
定义在类内部,但不在任何方法之内
被该类的所有实例对象所共享
可以通过类名或实例对象访问
用于定义与这个类相关的特征或属性
实例变量(Instance Variable)
定义在类内部的方法之内
每个实例对象拥有属于自己的变量副本
只能通过实例对象访问
用于定义实例对象的个性化特征或状态
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Person : species = 'Human' def __init__ (self, name ): self .name = name p1 = Person('John' ) p2 = Person('Mary' ) print (p1.species) print (p2.species) print (p1.name) print (p2.name)
综上,类变量用于定义类的通用属性,实例变量用于定义实例的独特属性。区分二者是理解Python面向对象的关键。
创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Employee : '所有员工的基类' empCount = 0 def __init__ (self, name, salary ): self .name = name self .salary = salary Employee.empCount += 1 def displayCount (self ): print "Total Employee %d" % Employee.empCount def displayEmployee (self ): print "Name : " , self .name, ", Salary: " , self .salary
类函数必有参数 ‘self’ 必须有一个额外的第一个参数名称, 按照惯例它的名称是 self,self 不是 python 关键字,换成其他词语也行。
创建实例对象与访问 1 2 emp1 = Employee("Zara" , 2000 ) emp1.displayEmployee()
继承 通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类。
继承语法 class 派生类名(基类名)
调用基类 调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。区别在于类中调用普通函数时并不需要带上 self 参数 ,这点在代码上的区别如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Base : def base_method (self ): print ("Base method" ) class Derived (Base ): def derived_method (self ): Base.base_method(self ) print ("Hello" ) d = Derived() d.derived_method() Base method Hello
在Derived类中:
区别在于:
调用基类方法需要指明方法所属的基类
基类方法需要传入self,指代实例自己
而对于普通函数,只需要直接调用即可,不需要self参数。
这与Python的名称空间和面向对象实现有关,是理解Python类继承的关键点。
运算符重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 __init__ : 构造函数,在生成对象时调用 __del__ : 析构函数,释放对象时使用 __repr__ : 打印,转换 __setitem__ : 按照索引赋值 __getitem__: 按照索引获取值 __len__: 获得长度 __cmp__: 比较运算 __call__: 函数调用 __add__: 加运算 __sub__: 减运算 __mul__: 乘运算 __truediv__: 除运算 __mod__: 求余运算 __pow__: 乘方
torch.nn.Module
的 __call__
方法会调用 forward
方法,并且在调用 forward
之前和之后还会执行一些其他的操作,比如设置钩子(hooks)和调用 _check_forward_hooks
等。
以下是 torch.nn.Module
中 __call__
方法的简化实现逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Module : def __call__ (self, *input , **kwargs ): for hook in self ._forward_pre_hooks.values(): result = hook(self , input ) if result is not None : if not isinstance (result, tuple ): result = (result,) input = result result = self .forward(*input , **kwargs) for hook in self ._forward_hooks.values(): hook_result = hook(self , input , result) if hook_result is not None : result = hook_result return result
__call__
方法 :当你实例化一个 Module
并调用它时(例如 model(input)
),Python 会调用 __call__
方法。
forward
方法 :__call__
方法内部会调用 forward
方法,这是你需要在子类中重写的方法,用于定义前向传播的逻辑。
钩子(Hooks) :在调用 forward
之前和之后,__call__
方法会处理一些钩子。这些钩子可以用于调试、可视化或其他目的。
+= 在Python中可以通过特殊方法__iadd__来对+=符号进行重载。
__iadd__需要定义在类中,用于指定+=操作时的具体行为。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Vector : def __init__ (self, x, y ): self .x = x self .y = y def __iadd__ (self, other ): self .x += other.x self .y += other.y return self v1 = Vector(1 , 2 ) v2 = Vector(3 , 4 ) v1 += v2 print (v1.x, v1.y)
这里我们定义了__iadd__方法,用于实现两个Vector对象使用+=时的相加操作。
__iadd__方法的参数是另一个要相加的对象,在方法内部我们实现了两个向量的分量相加,并返回self作为结果。这样就实现了+=的运算符重载。
此外,Python还提供了__add__特殊方法用于重载+符号,但是__iadd__和__add__有以下区别:
__add__返回一个新的对象,不改变原有对象。
__iadd__在原有对象的基础上进行操作,并返回对原对象的引用。
所以对可变对象进行+=操作时,通常需要实现__iadd__方法。
参考文献 https://www.runoob.com/python/python-object.html