python@staticmethod@classmethod@property介绍与使用

Python面向对象编程中,类中定义的方法可以是 @classmethod 装饰的类方法,也可以是 @staticmethod 装饰的静态方法,用的最多的还是不带装饰器的实例方法。那我们该如何区分使用它们呢?

介绍

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) # self: <__main__.A object at 0x000001E596E41A90>
A.m2(1) # cls: <class '__main__.A'>
A.m3(1) # 111

类中共定义了3个方法,m1 是实例方法,第一个参数必须是 self(约定俗成的)。m2 是类方法,第一个参数必须是cls(同样是约定俗成),m3 是静态方法,参数根据业务需求定,可有可无。

实例方法self

print(A.m1)
# A.m1在py2中显示为<unbound method 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参数,所以不再手动传递参数,这是调用实例方法的过程。

A.m1(a, 1)
# 等价  
a.m1(1)

类方法classmethod

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。

A.m2(1) 
 # 等价
a.m2(1)

这使得我们可以在实例方法中通过使用 self.m2()这种方式来调用类方法和静态方法。

def m1(self, n):
    print("self:", self)
    self.m2(n)

静态方法staticmethod

print(A.m3)
<function A.m3 at 0x000002BF7FF9A840>

print(a.m3)
<function A.m3 at 0x000002BF7FF9A840>

m3是类里面的一个静态方法,跟普通函数没什么区别,与类和实例都没有所谓的绑定关系,它只不过是碰巧存在类中的一个函数而已。不论是通过类还是实例都可以引用该方法。

A.m3(1) 
 # 等价
a.m3(1)

使用场景

1. @staticmethod

staticmethod用于修饰类中的方法,使其可以在不创建类实例的情况下调用方法,这样做的好处是执行效率比较高。当然,也可以像一般的方法一样用实例调用该方法。该方法一般被称为静态方法。静态方法不可以引用类中的属性或方法,其参数列表也不需要约定的默认参数self。我个人觉得,静态方法就是类对外部函数的封装,有助于优化代码结构和提高程序的可读性。当然了,被封装的方法应该尽可能的和封装它的类的功能相匹配。 这里给出一个样例来直观的说明一下其用法:

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))
#结果为5 5

2. @classmethod

classmethod好处就是你以后重构类的时候不必要修改构造函数,只需要额外添加你要处理的函数,然后使用装饰符 @classmethod 就可以

class C:
    @classmethod
    def f(cls, arg1, arg2, ...): ...

当不使用@classmethod时处理字符串格式问题

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()
# 2022
# 1
# 1

当使用@classmethod时处理字符串格式问题

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)
        # 返回的是一个初始化后的class
        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()
# 2022
# 1
# 1

@classmethod在继承时更好用。类方法的一个主要用途就是定义多个构造器:在已经写好初始类的情况下,想给初始类再新添功能,不需要修改初始类,只要在下一个类内部新写一个方法,使用@classmethod装饰一个即可。

# 初始类
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):
        # 第一个参数cls,表示调用当前的类名
        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()

# 初始类Date不需要改变,新增功能handle_date;在Preprocess类里修改即可,Preprocess继承Date初始类

3. @property

可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改

使用场景1:

修饰方法,使func可以像属性一样访问

class DataSet(object):
    @property
    def method_with_property(self):  # 含有@property
        return 15
    def method_without_property(self):  # 不含@property
        return 15

l = DataSet()
print(l.method_with_property)  # 15
print(l.method_without_property())  # 15
# 加@property,可以用调用属性的方式来调用方法,后面不需要加()
# 没加@property , 必须使用正常的调用方法的形式,即在后面加()

如果使用property进行修饰后,又在调用的时候,方法后面添加了(), 那么就会显示错误信息:TypeError: ‘int’ object is not callable,也就是说添加@property 后,这个方法就变成了一个属性,如果后面加入了(),那么就是当作函数来调用,而它却不是callable(可调用)的。

使用场景2:

与所定义的属性配合使用,这样可以防止属性被修改
由于python进行属性定义时,没办法设置私有属性,因此要通过@property的方法来进行设置。这样可以隐藏属性名,让用户进行使用的时候无法随意修改。

class DataSet(object):
    def __init__(self):
        self._images = 1
        self._labels = 2 #定义属性的名称
    @property
    def images(self): #方法加入@property后,这个方法相当于一个属性,这个属性可以让用户进行使用,而且用户有没办法随意修改。
        return self._images 
    @property
    def labels(self):
        return self._labels
l = DataSet()
#用户进行属性调用的时候,直接调用images即可,而不用知道属性名_images,因此用户无法更改属性,从而保护了类的属性。
print(l.images) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。

参考连接:
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

  • 1
    点赞
  • 3
    收藏
  • 打赏
    打赏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

整天梦游的小jerry

感谢支持~笔芯❤️

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值