小编已经在内测抽奖系统了,快来填问卷、赢礼品吧!链接:http://vote.51testing.com/ (本次礼包好物多多:大容量马克杯、畅销测试书籍)
在初学Python过程中,会遇到这样的概念,一个类下面会有多个方法,有的叫类方法、有的叫静态方法,还有的叫实例方法。当调用他们的时候,不免会有点蒙圈,那么他们之间的区别是什么呢?
和类属性一样,类方法可以进行细致地划分为类方法、实例方法和静态方法。
表象区别就是:
类方法前用@classmethod修饰
静态方法前用@staticmethod修饰
不加任何修饰的就是实例方法(普通方法)
用法区别
实例方法
也是普通方法,实例方法是我们最常用的方法,它定义时最少要包含一个self参数,用于绑定调用此方法的实例对象(所谓绑定,即用实例调用的时候,不需要显式的传入)。
换句话说,当实例调用方法的时候,会默认将实例本身传入到这个参数self,而当类直接调用时,因为本身类型是一个class,不是实例对象,所以报错。
如果非要用类直接调用,需要手动传入一个实例作为第一个参数。注意:若随便传入一个字符串的话,也不会报错,但是会造成程序紊乱,因此不推荐这种调用方式。
用如下这段代码举例说明:
class A(object): def instance_method(self, X): print(f'instance_method :{X} : {self}') a = A() A.instance_method('x') A.instance_method(a, 'x') A.instance_method('strs', 'x') a.instance_method('a')
代码中 instance_method 为实例方法,而第6行类A调用了此方法,而实例方法默认传入的应该是实例,而不是类,因此将x当做实例传给了默认的self,此时实例方法还缺少一个参数没有传入,导致报错:TypeError: instance_method() missing 1 required positional argument: 'x'。
第7行纠正了第6行的做法,传入了实例a,且传入了一个参数x,所以不会报错,self就是A的实例a。
第8行将字符串代替实际的实例a传入self,虽不会报错,但是对于程序毫无价值,不推荐这样使用,没有意义。
第9行是最常用的方法,实例a调用了实例方法,默认将实例a传入了self,再将参数x传入了X,完整实现了调用。
本地方法
就当做实例方法的一种吧,好奇心的驱使,如果实例方法没有添加self这个参数呢,为了区分,我们且叫他“本地方法”。所谓本地,也就是实例无法调用,只能类自己调用。
class A(object): def local_method(): print(f'local_method') def local_method2(strs): print(f'local_method2') a = A() a.local_method() a.local_method2() A.local_method()
如上代码,第2行的local_method()就是个本地方法,而此时在第9行实例a调用这个本地方法的时候,由于程序会默认将实例a传入参数self,但是本地方法没有写self,因此报错:TypeError: A.local_method() takes 0 positional arguments but 1 was given。
再看第5行的实例方法,为什么叫实例方法,明明没有self啊?这里要特别说明下,self只是约定俗成的写法,实际上随便写个什么字符串都可以的。因此第10行实例a调用实例方法,不会报错,程序正常执行。
第11行类A调用本地方法,也是不会报错的。但如果类A调用实例方法就和第一节讲的报错了。
类方法
类方法有一个特点,就是这个方法必须要有@classmethod来修饰。和实例方法类似,至少也要传一个参数,约定俗称为cls,Python会自动将类本身绑定到这个参数上面。
类方法通常使用类直接调用,也可以用实例调用(不推荐)。当实例调用的时候,Python会将实例的最底层类(即实例直接所属类)型传入cls参数中。
class A(object): name = 'I am Class A' @classmethod def class_method(cls, s): print(cls.name) # 可以访问类成员print(cls.name) # 可以访问类成员 print(f"class_method : {cls} :: {s}") class B(A): name = 'I am Class B' a = A() b = B() A.class_method('I am class') a.class_method('I am instance') B.class_method('I am B class') b.class_method('I am b instance')
如上代码,B类继承了A类,并复写了name属性,而此时A类中的方法就是类方法,有两个参数,一个是默认的类参数cls,还有一个普通入参。
第14行,A类直接调用自己的类方法,默认将自己传入了cls,并将括号中的字符串传给了参数s,用得恰到好处。此时第6行打印“I am Class A”可以看出,cls确实是传的A。
第15行用A的实例a调用类方法,会默认将a的直属类(也就是A)传到cls中,因此效果和A调用是一样的。
第16行用继承类B调用的父类的类方法,既然是继承,那么程序传入的就是类B到cls中,由于B类中对name做了复写,因此第6行打印出来的就是“I am Class B”。
第17行用继承类B的实例b调用的父类的类方法,按照上述规则,是传入的b的直属类到cls中,也就是将B传入了cls中,而不是A(这边要注意区别),因此和B调用是一样的。
静态方法
静态方法是使用@staticmethod修饰的方法,它不能访问任何类属性和类方法,因为它不含self或cls这些特殊参数,因此也无法访问类或实例中的成员。
也就是说,Python没有给他绑定实例或者类,要想使用,只能当参数来传,所以在静态方法中的入参都是普通参数,严格来讲,上面说的本地方法应该也要写成静态方法。
class A(object): @staticmethod def static_method(a, b): print(f"static_method : {a} + {b}") a = A() A.static_method('aa', 'bb') a.static_method('aa', 'bb')
如上代码中,尽管第7行类A调用了方法,但是由于是静态方法,访问不了类属性,因此不会将类A传入所谓的cls中,静态方法中也没有cls这个参数,因此它的参数都是普通入参。
第8行的实例调用也是和A一样的效果。
所以逻辑上讲,类方法应当只被类调用,实例方法只被实例调用,静态方法两者都能调用,主要区别在于参数传递上的区别。
实例方法悄悄传递的是self引用作为参数,而类方法悄悄传递的是cls引用作为参数。
要记住几点
1.实例调用实例方法和本地方法时,Python默认将实例本身作为第一个参数传入。
2.实例调用类方法时,Python默认将实例的最底层类作为第一个参数传入。
3.实例调用静态方法时,Python啥也不传,需要几个参数就要传几个参数。
4.类调用类方法时,Python默认将类本身作为第一个参数传入。
5.类调用非类方法时,Python啥也不传,需要几个参数就要传几个参数。
作者:王小健