Python面向对象编程中,类中定义的方法可以是 @classmethod 装饰的类方法 ,也可以是 @staticmethod 装饰的静态方法 ,用的最多的还是不带装饰器的实例方法。那我们该如何区分使用它们呢?
介绍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class A (object ): def m1 (self, n ): print ("self:" , self) @classmethod def m2 (cls, n ): print ("cls:" , cls) @staticmethod def m3 (n ): print ('111' ) a = A() a.m1(1 ) A.m2(1 ) A.m3(1 )
类中共定义了3个方法,m1 是实例方法,第一个参数必须是 self(约定俗成的)。m2 是类方法,第一个参数必须是cls(同样是约定俗成),m3 是静态方法,参数根据业务需求定,可有可无。
实例方法self
1 2 3 4 5 6 print (A.m1)<function A.m1 at 0x000002BF7FF9A488 > print (a.m1)<bound method A.m1 of <__main__.A object at 0x000002BF7FFA2BE0 >>
A.m1是一个还没有绑定实例对象的方法,对于未绑定方法,调用 A.m1 时必须显示地传入一个实例对象进去,而 a.m1是已经绑定了实例的方法,python隐式地把对象传递给了self参数,所以不再手动传递参数,这是调用实例方法的过程。
1 2 3 A.m1(a, 1) # 等价 a.m1(1)
类方法classmethod
1 2 3 4 5 print(A.m2) <bound method A .m2 of <class '__main__ .A '>> print (a.m2) <bound method A .m2 of <class '__main__ .A '>>
m2是类方法,不管是 A.m2 还是 a.m2,都是已经自动绑定了类对象A的方法,对于后者,因为python可以通过实例对象a找到它所属的类是A,找到A之后自动绑定到 cls。
这使得我们可以在实例方法中通过使用 self.m2()这种方式来调用类方法和静态方法。
1 2 3 def m1(self, n): print("self:", self) self.m2(n)
静态方法staticmethod
1 2 3 4 5 print(A.m3) <function A.m3 at 0x000002BF7FF9A840> print(a.m3) <function A.m3 at 0x000002BF7FF9A840>
m3是类里面的一个静态方法,跟普通函数没什么区别,与类和实例都没有所谓的绑定关系,它只不过是碰巧存在类中的一个函数而已。不论是通过类还是实例都可以引用该方法。
使用场景
@staticmethod
staticmethod用于修饰类中的方法,使其可以在不创建类实例的情况下调用方法,这样做的好处是执行效率比较高。当然,也可以像一般的方法一样用实例调用该方法。该方法一般被称为静态方法。静态方法不可以引用类中的属性或方法,其参数列表也不需要约定的默认参数self。我个人觉得,静态方法就是类对外部函数的封装,有助于优化代码结构和提高程序的可读性。当然了,被封装的方法应该尽可能的和封装它的类的功能相匹配。 这里给出一个样例来直观的说明一下其用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 class Time (): def __init__ (self,sec ): self.sec = sec @staticmethod def sec_minutes (s1,s2 ): return abs (s1-s2) t = Time(10 ) print (Time.sec_minutes(10 ,5 ),t.sec_minutes(t.sec,5 ))
@classmethod
classmethod好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以
1 2 3 class C : @classmethod def f (cls, arg1, arg2, ... ): ...
当不使用@classmethod时处理字符串格式问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class DateMethodBefore : def __init__ (self,year=0 ,month=0 ,day=0 ): self.day=day self.month=month self.year=year def print_date (self ): print (self.year) print (self.month) print (self.day) string_date = '2022-01-01' year, month, day = map (int , string_date.split('-' )) dd = DateMethodBefore(year, month, day) dd.print_date()
当使用@classmethod时处理字符串格式问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class DateMethodAfter : def __init__ (self, year=0 , month=0 , day=0 ): self.day = day self.month = month self.year = year @classmethod def deal_date (cls, string_date ): year, month, day = map (int , string_date.split('-' )) date1 = cls(year, month, day) return date1 def print_date (self ): print (self.year) print (self.month) print (self.day) dd = DateMethodAfter.deal_date("2022-01-01" ) dd.print_date()
@classmethod在继承时更好用。类方法的一个主要用途就是定义多个构造器:在已经写好初始类的情况下,想给初始类再新添功能,不需要修改初始类,只要在下一个类内部新写一个方法,使用@classmethod装饰一个即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Date : def __init__ (self, year=0 , month=0 , day=0 ): self.day = day self.month = month self.year = year def print_date (self ): print (self.day) print (self.month) print (self.year) class Preprocess (Date ): @classmethod def handle_date (cls, string_date ): year, month, day = map (int , string_date.split('-' )) after_date = cls(year, month, day) return after_date S = Preprocess.handle_date("2022-1-1" ) S.print_date()
@property
可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改
使用场景1:
修饰方法,使func可以像属性一样访问
1 2 3 4 5 6 7 8 9 10 11 12 class DataSet (object ): @property def method_with_property (self ): return 15 def method_without_property (self ): return 15 l = DataSet() print (l.method_with_property) print (l.method_without_property())
如果使用property进行修饰后,又在调用的时候,方法后面添加了(), 那么就会显示错误信息:TypeError: ‘int’ object is not callable,也就是说添加@property 后,这个方法就变成了一个属性,如果后面加入了(),那么就是当作函数来调用,而它却不是callable(可调用)的。
使用场景2:
与所定义的属性配合使用,这样可以防止属性被修改
由于python进行属性定义时,没办法设置私有属性,因此要通过@property的方法来进行设置。这样可以隐藏属性名,让用户进行使用的时候无法随意修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 class DataSet (object ): def __init__ (self ): self._images = 1 self._labels = 2 @property def images (self ): return self._images @property def labels (self ): return self._labels l = DataSet() print (l.images)
参考连接:
https://zhuanlan.zhihu.com/p/28010894
https://blog.csdn.net/Liquor6/article/details/122440364
https://zhuanlan.zhihu.com/p/64487092
https://blog.sciencenet.cn/blog-3428464-1257579.html
https://cloud.tencent.com/developer/article/1597015
python@staticmethod@classmethod@property介绍与使用