13 面向对象
13.1 基础概念
- 类:
类是具有一系列共同特征和行为的事件抽象概念集合。类所描述的概念和现实生活中的类概念非常相似。例如生物有很多种类,食物也有不同的种类,商品也有很多不同的种类。而一个类通过细分又可以划分各种小的分类,如动物就可以分为陆生动物、水生动物和两栖动物等等,被划分到同一个类,其都有着相似的特征和行为方式。
- 实例化对象
实例对象通常被定义某一个类事物的具体个体,是该类事物的一个具体表现。如人是一个类,”张三“就是人这个类的实例对象,因为他代表了一个具体类个体。
- 类变量
类变量一般是定义类中,但不在类方法中的变量,可以在整个实例化对象中公用。
- 类属性
类变量或实例变量,常用于描述实例对象的特征,如张三的姓名、身高等
- 方法:
以不严谨的方式来讲,就是定义在类中的函数称之为方法,常用于描述实例对象的行为,一般分为构造函数和常规函数。
- 继承
就是子类可以从父类获取父类定义的属性和方法等。
- 方法重写
如果父类的方法不能满足子类的要求,可以定义一个方法名与父类相同名字的方法,这个过程称之方法覆写(如Java中@Override)或方法重写
- 实例变量
类是一个非常抽象的概念,而实例则代表一个具体的对象,而与对象绑定的变量可称之为实例变量。
13.2 类定义
在Python中一个简单的类定义格式如下所示:
class Person(object):
"""
定义一个人类
"""
# 类变量
classVariable="sample"
# 构造函数,也称实例化对象方法
def __init__(self,name,age):
self.name=name
self.age=age
# 类中定义的方法
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
详细解释如下所示:
- 定义一个类的关键字为class
- Person:定义的类名称,首字母必须大写
- (object):表示定义的类所继承的父类,这里为object
- 三引号中的字符为文档字符串,用于描述类的功能和作用
- classVariable:类变量,所有实例对象可共享该变量
- _init_:构造函数,用于实例化一个对象前,做一些的特征描述
- name,age:实例化对象本身所具有的特征
- getPersonInfo:类中定义的方法,用于描述类的行为,一般常以动词做为开头
- self:代表实例化对象本身,相当于Java/C#中的this
13.3 面向对象三大特性
面向常见的三大特性:封装、继承、多态。分别如下所示:
13.3.1 封装
封装主要将类的属性和方法打包为一个整体,对外部而言,仿佛是不可见的。其主要特点如下所示:
- 1.对象的行为对于外部来说是不可见的,即对象的状态信息是私密的。
- 2.客户端不能通过直接操作来改变对象内部状态。相反,客户端需要通过发送消息来请求对象改变其内部状态。对象根据请求的类型,通过特定的方法改变内部状态,以做出响应
- 3.在Python中,封装(属性和方法的隐藏)的概念不是隐式的,因为它没有提供诸如C系列语言的关键字public/private/protected。
单下划线和双下划线在Python变量和方法名中都有其特殊的含义,一部分仅仅作为一种约定,一部分则含有特殊意义,详细如下所示:
- 前置下划线:
前置下划线通常代表一种约定,表示仅在内部使用,通过针对编写程序员,可以简单理解为其他语言的private关键字。
- 后置下划线
通常用于解决命名冲突,在Python中,str是一个将其他类型转换为字符串的函数,那如果写str_,则可以代表重新定义一个变量或方法,在PyQT中,这种用法比较多。
- 前置双下划线
在类环境中使用时会触发名称改写,对Python解释器有特殊含义
- 前后双置下划线
前后双置下划线的方法被称之为魔法方法,表示则Python语言本身定义的特殊方法,在自定义的属性中要避免使用这种命名方式。
- 单下线
通常用作临时或无意义变量名称,另外也可以表示Python REPL会话中上一个表达式的结果。
13.3.2 继承
继承通常是指子类从父类中获取父类的数据和方法等等。其主要特点如下所示:
- 1.继承表示一个类可以继承父类的大部分功能
- 2.继承可以复用父类已经定义的功能并可以对父类原有功能进行扩展
- 3.继承可以利用不同类的对象之间建立层次结构,在Python支持多重继承。
13.3.3 多态
多态是指不同的对象可以执行相同的动作,但却要通过自己的实现代码来执行。国粹京剧之前都是子承父业,代代相传的艺术。假设有一对父子,父亲是非常有名的京剧艺术家,儿子长大成人,模仿父亲的戏也惟妙惟肖。有一天父亲生病,导致无法上台,而父亲的戏票已经卖出,如果退票则损失太大。因为京剧都是需要化妆后才上台,那是不是儿子可以代替父亲上台?在这里需要注意以下几点:
- 子类以父类身份出现
儿子代替父亲出演,化妆上台后就是父亲身份了。
- 子类工作时必须以自己的方式实现
儿子模仿再像也是模仿,只能以自己的理解来出演父亲的作品
- 子类以父类的身份出现时,子类特有的属性和方法不可以使用
儿子经过多年学习,其实已经有了自己独特的表演模式和行为习惯,但代替父亲出演时,是不能以自己的表演方式出演的。必须按父亲的风格去表演。
13.4 创建类
我们先来创建一个类,示例如下所示:
class Person:
def __init__(self,name,age):
self.name=name
self.age=age
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
13.5 创建类的实例对象
创建类的实例对象就是根据类创建具体的个体,如下所示:
person=Person("Surpass",28)
以上就完成了根据Person类实例化一个具体的人,其名字为Surpass,年龄为28,在创建实例后,我们就可以访问类的属性和方法,通过方式为:
实例对象.[属性或方法]
13.6 使用类和实例
13.6.1 访问属性
person=Person("Surpass",28)
print(f"name is {person.name},age is {person.age}")
13.6.2 访问方法
person=Person("Surpass",28)
print(f"person info is {person.getPersonInfo()}")
13.6.3 创建多个实例
既然类是一种事件的抽象集合,那当然也可以根据创建n个实例对象了,如下所示:
personA=Person("Surpass",28)
personB=Person("Kevin",38)
personB=Person("Lisa",20)
13.6.4 为属性指定默认值
类中的每个属性必须有初始值,对于一些经常无需要每次赋值的属性可以指定默认值,如下所示:
class Person:
def __init__(self,name,age=28):
self.name=name
self.age=age
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
# 实例化对象
personA=Person("Surpass")
看看上面的用法是不是跟函数使用很像。
13.6.5 修改属性值
如果需要修改属性的值,可以使用以下方法进行修改
- 1.通过实例进行修改
- 2.通过调用方法进行修改
1.通过实例进行修改
示例代码如下所示:
class Person:
def __init__(self,name,age=28):
self.name=name
self.age=age
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
person=Person("Surpass",28)
print(f"Before modify property {person.name} {person.age}")
person.name="Lisa"
person.age=20
print(f"After modify property {person.name} {person.age}")
输出结果如下所示:
Before modify property Surpass 28
After modify property Lisa 20
2.通过调用方法进行修改
通过实例修改直接修改属性值非常方便,如下所示:
class Person:
def __init__(self,name,age=28):
self.name=name
self.age=age
def getName(self):
return self.name
def setName(self,name):
self.name=name
def getAge(self):
return self.age
def setAge(self,age):
self.age=age
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
person=Person("Surpass",28)
print(f"Before modify property {person.name} {person.age}")
person.setName("Lisa")
person.setAge(20)
print(f"After modify property {person.name} {person.age}")
输出结果如下所示:
Before modify property Surpass 28
After modify property Lisa 20
熟悉Java的小伙伴同学,一眼就看出来这种跟Java很像,而更地道的Python写法如下所示:
class Person:
def __init__(self,name,age=28):
self._name=name
self._age=age
@property
def name(self):
return self._name
@name.setter
def name(self,value):
if not isinstance(value,(str,)):
raise TypeError("Expect str")
else:
self._name=value
@name.deleter
def name(self):
raise AttributeError("Can not delete attribute")
@property
def age(self):
return self._age
@age.setter
def age(self,value):
if not isinstance(value,(int,)):
raise TypeError("Expect a numer")
elif value<0:
self._age=0
else:
self._age=value
@age.deleter
def age(self):
raise AttributeError("Can not delete attribute")
def getPersonInfo(self):
return f"name is {self.name},age is {self.age}"
def run(self):
return f"{self.name} is running"
person=Person("Surpass",28)
print(f"Before modify property {person.getPersonInfo()} ")
person.name="Lisa"
person.age=-123
print(f"After modify property {person.getPersonInfo()}")
print(f"测试异常情况")
person.name=98
person.age="098"
输出结果如下所示:
Before modify property name is Surpass,age is 28
After modify property name is Lisa,age is 0
测试异常情况
Traceback (most recent call last):
File "C:/Users/Surpass/Documents/PycharmProjects/SADCI/TempCode/basicCode.py", line 52, in <module>
person.name=98
File "C:/Users/Surpass/Documents/PycharmProjects/SADCI/TempCode/basicCode.py", line 15, in name
raise TypeError("Expect str")
TypeError: Expect str
以上这种方法,必须getter、setter、deleter三个方法的名称必须一样,且必须先创建getter,后面的setter、deleter才能访问和使用,那如果已经事先定义好了getter、setter、deleter的方法,还有没有办法实现以上这种做法?答案当然是肯定的,如下所示:
class Person:
def __init__(self,name,age=28):
self._name=name
self._age=age
def getName(self):
return self._name
def setName(self,value):
if not isinstance(value,(str,)):
raise TypeError("Expect str")
else:
self._name=value
def delName(self):
raise AttributeError("Can not delete attribute")
def getAge(self):
return self._age
def setAge(self,value):
if not isinstance(value,(int,)):
raise TypeError("Expect a numer")
elif value<0:
self._age=0
else:
self._age=value
def delAge(self):
raise AttributeError("Can not delete attribute")
ageProperty=property(getAge,setAge,delAge)
nameProperty=property(getName,setName,delName)
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
person=Person("Surpass",28)
print(f"Before modify property {person.getPersonInfo()} ")
person.setName("Lisa")
person.setAge(-123)
print(f"After modify property {person.getPersonInfo()}")
print(f"测试异常情况")
person.setName(98)
输出结果如下所示:
Before modify property name is Surpass,age is 28
After modify property name is Lisa,age is 0
测试异常情况
Traceback (most recent call last):
File "C:/Users/Surpass/Documents/PycharmProjects/SADCI/TempCode/basicCode.py", line 49, in <module>
person.setName(98)
File "C:/Users/Surpass/Documents/PycharmProjects/SADCI/TempCode/basicCode.py", line 13, in setName
raise TypeError("Expect str")
TypeError: Expect str
以上通过方法来修改属性的思想就是面向对象中的封装思想。但在Python,如果一个类并不存在某个属性,通过对象.属性名可以添加一个新的属性,但这种做法破坏了面向对象中的封装特性,因此不建议使用。如下所示:
class Person:
def __init__(self,name,age=28):
self._name=name
self._age=age
@property
def name(self):
return self._name
@name.setter
def name(self,value):
if not isinstance(value,(str,)):
raise TypeError("Expect str")
else:
self._name=value
@name.deleter
def name(self):
raise AttributeError("Can not delete attribute")
@property
def age(self):
return self._age
@age.setter
def age(self,value):
if not isinstance(value,(int,)):
raise TypeError("Expect a numer")
elif value<0:
self._age=0
else:
self._age=value
@age.deleter
def age(self):
raise AttributeError("Can not delete attribute")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
person=Person("Surpass",28)
# 通过实例直接增加一个新的属性,不推荐
person.country="China"
print(f"add new person property : {person.country}")
输出结果如下所示:
add new person property : China
13.7 属性
属性常用于描述类的特征,可以通过类中定义的方法访问,也可以通过类或实例进行访问。因因此属性又可分为类属性和实例属性。
13.7.1 实例属性
实例属性,通俗来讲是通过self.变量名称定义的属性,有时也称实例变量,属于特定的实例对象。示例如下代码如下所示:
class Person:
def __init__(self,name,age):
# 实例属性赋值
self._name=name
self._age=age
# 获取实例属性值
def getName(self):
return self._name
# 实例属性需要与实例对象进行绑定后才能访问
person=Person("Surpass",28)
print(f"{person.getName()}")
13.7.2 类属性
Python允许声明属于类本身的变量称之为类属性或类变量,类属性无需实例化对象即可,如下所示:
class Person:
# 类属性
classVar="类属性"
def __init__(self,name,age):
# 实例属性赋值
self._name=name
self._age=age
# 获取实例属性值
def getName(self):
return self._name
# 实例属性需要与实例对象进行绑定后才能访问
person=Person("Surpass",28)
# 访问实例属性-通过实例
print(f"访问实例属性-通过实例:{person.getName()}")
# 访问类属性-通实例
print(f"访问类属性-通实例:{person.classVar}")
# 访问类属性
print(f"访问类属性:{Person.classVar}")
输出结果如下所示:
访问实例属性-通过实例:Surpass
访问类属性-通实例:类属性
访问类属性:类属性
13.7.3 私有属性
Python没有Java/C#语言的权限控制字段,如public/protected/private等等,通常的约定是以单下划线开头定义的属性为私有属性,如下所示:
class Person:
# 类私有属性
_classVar="类属性"
def __init__(self,name,age):
# 实例属性赋值
self._name=name
self._age=age
13.7.4 特殊属性
Python对象中包含很多开始和结尾以双下划线的开始和结束的方法,称为特殊属性,常见的特殊属性如下所示:
特殊方法 | 含义 | 示例 |
---|---|---|
obj. __dict__ | 对象的属性字典 | int.__dict__ |
instance.__class__ | 对象所属的类 | int.__class__ |
class.__bases__ | 类的基类元组 | int.__bases__ |
class.__base__ | 类的基类 | int.__base__ |
13.7.5 小结
- 1.实例属性只能通过实例对象进行访问
- 2.类属性属于类和实例对象共享的变量,无需要实例化即可访问,在实例化对象也可以通过实例对象进行访问
- 3.如果要定义一个私有,只需要在变量加一个下划线即可
- 4.面向对象编程的封装特性原则上要求不直接访问类中的属性,Python中可以通过定义私有属性,然后定义相应的方法来访问和修改私有属性,也可以使用装饰器@property来装饰这些函数,达到程序将函数当做属性访问。
13.8 继承
在创建一个类,并不需要每次都新建。如果已经现成的类,其已有功能能满足你的要求,那么则可以使用继承来达到目的。一个类继承另一个类时,将自动获取另一个类的属性和方法。原有的类称为父类,新的类称为子类。子类除了继承父类的属性和方法,还可以定义自己的属性和方法,也可以重写父类的方法。
13.8.1 构造方法_init_()
创建子类的实例时,Python首先需要完成的任务是给父类的所有属性赋值。因此子类的方法_init_()需要父类施以援手。如下所示:
1.显式调用父类构造方法
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,):
super().__init__(name,age)
student=Student("Surpass",25)
print(student.getPersonInfo())
输出结果如下所示:
name is Surpass,age is 25
以上代码详解如下所示:
- super()._init_(name,age):显式声明调用父类构造方法,如果子类没有特殊属性,也可以省略
- student.getPersonInfo():子类调用父类继承过来方法
2.显式调用父类构造方法并添加自己的属性
示例如下所示:
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
super().__init__(name,age)
self._classNumber=classNumber
def getStudentInfo(self):
return f"name is {self._name},age is {self._age},classNumer is {self._classNumber}"
student=Student("Surpass",25,"Class-NO.1")
print(student.getStudentInfo())
输出结果如下所示:
name is Surpass,age is 25,classNumer is Class-NO.1
3.隐式调用父类构造方法
示例如下所示:
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
pass
student=Student("Surpass",25)
print(student.getPersonInfo())
输出结果如下所示:
name is Surpass,age is 25
通过以上几种,那可以猜测一下,是不是不管子类有没有声明构造方法,都是先调用父类构造方法呢?来看看以下示例:
1.父类和子类都有定义自己构造函数
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
self._name=name
self._age=age
self._classNumber=classNumber
print("2.调用子类构造方法")
def getStudentInfo(self):
return f"name is {self._name},age is {self._age},classNumer is {self._classNumber}"
student=Student("Surpass",25,"Class-No.1")
输出结果如下所示:
2.调用子类构造方法
2.父类有定义自己构造函数,子类未定义自己的构造函数
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
pass
输出结果如下所示:
1.调用父类构造方法
3.父类和子类都有定义自己构造函数,但子类显式调用父类构造方法
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
super().__init__(name,age)
self._classNumber=classNumber
print("2.调用子类构造方法")
def getStudentInfo(self):
return f"name is {self._name},age is {self._age},classNumer is {self._classNumber}"
student=Student("Surpass",25,"Class-NO.1")
输出结果如下所示:
1.调用父类构造方法
2.调用子类构造方法
通过以上代码,可以总结出以下结论:
- 1.在子类未定义构造函数,则隐式调用父类构造函数
- 2.在子类如果显式声明调用父类构造函数,则先调用父类构造函数,再调用子类的构造函数
- 3.如果子类自己定义了构造函数,则只调用子类本身的构造函数
- 4.如果需要显式调用父类构造函数,可以使用super(英文:superclass)关键字
- 5.擅用super方法,可以简化代码,当要调用父类构造方法或普通方法时,均可以使用该关键字
13.8.2 子类定义属性和方法
当一个类继承自父类后,父类的功能无法自身需求后,子类可以定义自己的属性和方法,示例如下所示:
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
super().__init__(name,age)
# 子类自身定义的属性
self._classNumber=classNumber
print("2.调用子类构造方法")
# 子类自身定义的方法,显式调用父类方法
def getStudentInfo(self):
return super().getPersonInfo()
# 子类自身定义的方法
def getStudentCount(self):
studentList=[]
studentList.append(self._name)
return len(studentList)
输出结果如下所示:
1.调用父类构造方法
2.调用子类构造方法
调用父类方法的结果:name is Surpass,age is 25
调用子类自身的方法:student count is 1
13.8.3 覆写父类方法
在Python中覆写父类方法非常简单,只需要让子类中方法名称与父类名称一致即可,如下所示:
class Person:
def __init__(self,name,age):
self._name=name
self._age=age
print("1.调用父类构造方法")
def getPersonInfo(self):
return f"name is {self._name},age is {self._age}"
def run(self):
return f"{self._name} is running"
class Student(Person):
def __init__(self,name,age,classNumber):
super().__init__(name,age)
# 子类自身定义的属性
self._classNumber=classNumber
print("2.调用子类构造方法")
# 子类自身定义的方法
def getStudentCount(self):
studentList=[]
studentList.append(self._name)
return len(studentList)
# 覆写父类已经定义的方法
def getPersonInfo(self):
return f"name is {self._name},age is {self._age},class number is {self._classNumber}"
student=Student("Surpass",25,"Class-NO.1")
print(f"调用子类自身的方法:student count is {student.getStudentCount()}")
print(f"调用子类覆写父类的方法:{student.getPersonInfo()}")
输出结果如下所示:
1.调用父类构造方法
2.调用子类构造方法
调用子类自身的方法:student count is 1
调用子类覆写父类的方法:name is Surpass,age is 25,class number is Class-NO.1
13.8.4 多重继承
像Java/C#等语言是单继承(即一个类同时只能继承一个父类,)和多接口继承(即一个类可继承多个接口),多层继承(因为继承的父类又继承自其他父类,看起来有多个层次),在Python中支持多重继承(即一个类可以同时继承多个类),示例如下所示:
class A:
def __init__(self):
print("1.父类构造方法-1")
def getMethonA(self):
print(f"1.父类方法{self.getMethonA.__name__}")
class B:
def __init__(self):
print("2.父类构造方法-2")
def getMethonB(self):
print(f"2.父类方法{self.getMethonB.__name__}")
class C:
def __init__(self):
print("3.父类构造方法-3")
def getMethonC(self):
print(f"3.父类方法{self.getMethonC.__name__}")
class D(A,B,C):
def getMethonD(self):
print(f"4.子类方法{self.getMethonD.__name__}")
d=D()
d.getMethonA()
d.getMethonB()
d.getMethonC()
d.getMethonD()
输出结果如下所示:
1.父类构造方法-1
1.父类方法getMethonA
2.父类方法getMethonB
3.父类方法getMethonC
4.子类方法getMethonD
使用多重继承有好也有坏
1.优点是可以调用多个父类的方法
2.缺点是如果继承的多个父类又属于同一个父类,则会出现二义性,另外也会造成逻辑上的复杂性增加,因此不推荐使用多重继承
13.9 方法
方法是与类相关的函数,Python用于定义一个方法格式如下所示:
def 方法名称(self,参数1,参数2...):
"""函数描述"""
函数体
return 返回值
方法的定义也前面的函数是一致,唯一的区别,是参数第一个位置必须是self,在Python方法常被分为构造方法、普通方法、类方法和静态方法。
13.9.1 构造方法
构造方法通常用于在实例化一个对象前,给对象传递一些必须的属性值,通常以__init__()做为标识符,一般格式如下所示:
class Person:
# 构造方法
def __init__(self,name,age):
self._name=name
self._age=age
13.9.2 普通方法
除了构造方法、类方法和静态方法之外的方法称之为普通方法,这种方法一般由用户自行定义的,示例如下所示:
class Person:
# 构造方法
def __init__(self,name,age):
self._name=name
self._age=age
# 普通方法
def getName(self):
return self._name
person=Person("Surpass",28)
print(f"访问普通方法:{person.getName()}")
输出结果如下所示:
访问普通方法:Surpass
13.9.3 类方法
类方法与类属性非常相似,是一种属于类的方法,不需要实例化就可以访问的一种方法,但需要使用装饰器@classmethon,其语法格式如下所示,注意与普通方法的区别在于第一个参数:
@classmethod
def 方法名称(cls,参数1,参数2...):
函数体
return 返回值
示例代码如下所示:
class Person:
className="这是类属性"
# 构造方法
def __init__(self,name,age):
self._name=name
self._age=age
# 普通方法
def getName(self):
return self._name
# 类方法,可以访问类属性,但不能访问对象属性
@classmethod
def getPersonInfo(cls,hello="Hello,"):
return f"{hello} {cls.className} "
person=Person("Surpass",28)
print(f"访问普通方法:{person.getName()}")
print(f"访问类方法:{Person.getPersonInfo('Welcome, ')}")
输出结果如下所示:
访问普通方法:Surpass
访问类方法:Welcome, 这是类属性
13.9.4 静态方法
静态方法即不需要传入self参数、也不需要传入cls参数,从面使得调用静态方法并不能获取得类中定义的属性和其他方法,但并不会影响类对象和实例对象的状态。静态方法有点像附属于类的对象的工具,与普通方法不同,调用静态方法时,只能通过类对象或实例对象,而不能脱离类对象使用,即静态方法被束缚在类对象中。其语法格式如下所示:
@staticmethod
def 方法名称(参数1,参数2...):
函数体
return 返回值
示例代码如下所示:
class Person:
className="这是类属性"
# 构造方法
def __init__(self,name,age,height,weight):
self._name=name
self._age=age
self._height=height
self._weight=weight
# 普通方法
def getName(self):
return self._name
# 类方法,可以访问类属性,但不能访问对象属性
@classmethod
def getPersonInfo(cls,hello="Hello,"):
return f"{hello} {cls.className} "
# 静态方法,不可以访问类属性
@staticmethod
def getBMI(h,w):
return w/((h/100)**2)
# 普通方法
def showBMI(self):
bmi=self.getBMI(self._height,self._weight)
return bmi
person=Person("Surpass",28,170,59)
print(f"访问普通方法:{person.getName()}")
print(f"访问类方法:{Person.getPersonInfo('Welcome, ')}")
print(f"访问静态方法:{Person.getBMI(170,59)}")
print(f"通过实例对象访问:{person.showBMI()}")
输出结果如下所示:
访问普通方法:Surpass
访问类方法:Welcome, 这是类属性
访问静态方法:20.41522491349481
通过实例对象访问:20.41522491349481
13.9.5 小结
- 1.类方法和静态方法都是通过装饰器实现,而构造方法和普通方法不昌
- 2.普通方法需要显式定义self参数,类方法需要定义cls参数,静态方法不需要self和cls参数
- 3.普通方法只能通过实例对象调用,类方法和静态方法可以通过类对象或实例对象调用,如果是使用实例对象调用的类方法和静态方法,最终都会转而通过类对象调用
- 4.普通方法使用最多,可以直接处理实例对象的各种逻辑;类方法不需要创建实例对象,直接处理类对象逻辑;静态方法将与类对象相关的部分逻辑抽离出来,可以用于测试,也方便后期维护代码
- 5.普通方法和类方法,可以改变实例对象或类对象的状态,而静态方法不能。
本文地址:https://www.cnblogs.com/surpassme/p/12983492.html
本文同步在微信订阅号上发布,如各位小伙伴们喜欢我的文章,也可以关注我的微信订阅号:woaitest,或扫描下面的二维码添加关注: