对面向对象的一点理解

之前在学习python面向对象的时候,有些教程中会讲解“类方法”、“静态方法”、“属性方法”这些概念,但是在初期开发python项目的时候感觉这些概念用不上,只是使用“纯粹”的面向对象也能写出一个能用的工具来;然而看到有些开发者的代码中会经常遇到这些,那这几个概念该如何去理解呢?以及会在哪些场景下会用到?于是便有了此文

阅读前提醒

本人对这些概念了解的并不深,如以下内容有错误请自行忽略

曾经在某篇文章中提到这三种方法的本质,它们其实是一种语法糖,也就是即使不用他们也依然可以写出一个python项目,但是用了这些语法糖后,可以让我们写的代码更具可读性

还有曾在某个视频教程也提及这些概念,那个视频中提到平时用的比较多的是属性方法,类方法和静态方法用的很少。一开始对这个说法没有什么概念,但随着编写的代码的增多,对这个说法也有同感。

之所以会有这样的结论,个人认为问题主要还是python的动态特性导致的,比如静态方法可以这样写:

Python
class Student:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def report1(self):
        print(f"My name is {self.name},my age is {str(self.age)}")

    def report2(self):
        self.static_report(self.name, self.age)

    @staticmethod
    def static_report(name: str, age: int):
        print(f"My name is {name},my age is {str(age)}")


stu = Student('gcc', '18')
stu.report1()
stu.report2()
但是,如果你是这样写的话,也能起到一样的效果

Python
class Student:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def report1(self):
        print(f"My name is {self.name},my age is {str(self.age)}")


def func_report(name: str, age: int):
    print(f"My name is {name},my age is {str(age)}")


stu = Student('gcc', '18')
stu.report1()
func_report(stu.name, stu.age)

由于python是一个多范式的语言,既可以写出面向对象的代码,也可以写出面向过程的代码,再加上其动态特性,所以导致有些特性在python中体现的并不明显。

再比如类方法,可以参考下面的代码

Python
class Student:
    num = 0

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    @classmethod
    def add(cls):
        cls.num += 1


stu = Student('gcc', '18')
stu.add()
print(Student.num)
print(stu.num)
Student.num += 1
print(Student.num)
print(stu.num)

可以发现,如果我们尝试在类的外边直接修改num,也依然可以起到一样的效果,所以这就导致类方法在python中体现的并不是很明显

属性方法其实也是类似,比如下面的代码:

Python
class Student:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        self.__stu_id = 0
        self.stu_id2 = 0

    @property
    def stu_id(self):
        return self.__stu_id

    @stu_id.setter
    def stu_id(self, value):
        self.__stu_id = value


stu = Student('gcc', '18')
print(stu.stu_id)
print(stu.stu_id2)
stu.stu_id = 2
stu.stu_id2 = 2
print(stu.stu_id)
print(stu.stu_id2)
结果可以发现,stu_id是属性方法,对外表现出是一个变量,但如果只是简单的使用属性方法,它和直接修改某个对象中的变量效果是等同的

总结一下:至少在我现有的认知中,类方法、静态方法、属性方法可以起到一些作用,但并不多,所以如果你的项目中几乎没有用到这些特性,个人觉着也用不着怀疑自己的面向对象学的是否扎实,也许在某些项目中,哪怕一点都用不到这些特性也几乎不影响你的开发1

但是

上面我有提了一句,某个视频教程提到属性方法用的相对多一些,那如果真要用属性方法,在哪些场景下会用到,并且只有属性方法更好的去做开发呢?

个人目前遇到的一个场景会用到,而且比其他方法更有优势:数据校验

直接上代码:

Python
class ConfigModel:
    def __init__(self) -> None:
        self.__age = 0
        self.__file_type = 'docx'

    @property
    def age(self):
        return self.__age

    @age.setter
    def age(self, value):
        if 0 < value < 80:
            self.__age = value
        else:
            raise ValueError("年龄必须在0-80岁之间")

    @property
    def file_type(self):
        return self.__file_type


model = ConfigModel()
print(model.age)
print(model.file_type)
model.age = 18
print(model.age)
try:
    model.age = 100
except ValueError as e:
    print(e)
try:
    model.file_type = 'ppt'
except Exception as e:
    print(e)
Python
class ConfigModel:
    def __init__(self) -> None:
        self.__age = 0
        self.__file_type = 'docx'

    def age(self):
        return self.__age

    def set_age(self, value):
        if 0 < value < 80:
            self.__age = value
        else:
            raise ValueError("年龄必须在0-80岁之间")

    def file_type(self):
        return self.__file_type


model = ConfigModel()
print(model.age())
print(model.file_type())
model.set_age(18)
print(model.age())
try:
    model.set_age(100)
except ValueError as e:
    print(e)

相比较来看,使用属性方法看起来会比使用方法/函数看起来更直观,而且在我们的认知中,age更应该做一个变量而非一个方法/函数,虽然也能表达同样的效果,但总体看着更符合认知一些

之所以没有提供直接通过修改对方的变量达到同样的效果,是因为直接修改变量往往很难被控制,即使这个项目是你一个人写的,随着时间的推移、项目规模的增大,保不住哪一天忘记了某个变量的作用以及被限定的范围,说不准就能写出一个age=100的情况,结果莫名出现一些问题,需要花费很长的时间debug才能找到问题的根源

善用属性方法,可以将各个变量的"权限"得到有效的控制,避免被乱调用


  1. 在以下这种场景下你可能不得不用静态方法或类方法:

    在某个类中写了一个方法,但从来没有调用这个类中的其他变量或方法,此时IDE提示可以变成一个函数,这个时候你“不得不”用一个@staticmethod去除这个提示(当然如果你不在乎IDE的警告就另当别论