Python基础-13面向对象

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,或扫描下面的二维码添加关注:
MyQRCode.jpg

posted @ 2020-05-28 19:48  Surpassme  阅读(122)  评论(0编辑  收藏