python 类与对象的详细用法


当前版本:

  • Python 3.8.4

简介

    类是一种自定义的数据类型,用于创建对象。类定义了一个对象的属性(也称为数据成员)和方法(也称为成员函数)。对象是类的实例化,是具体的数据实体,可以访问类中定义的属性和方法。

        

文章目录如下

1. 类与对象的作用

2. 基础用法

2.1. 定义类

2.2. 定义属性

2.3. 定义方法

3. 类的继承

3.1. 单继承

3.2. 多继承

3.3. 连续继承

3.4. 多态性

4. 类的装饰器

4.1. 类方法 @classmethod

4.2. 静态方法 @staticmethod

4.3. 类方法与静态方法的区别

5. 应用场景

5.1. 汽车案例

5.2. 电视案例


        

1. 类与对象的作用

类与对象的作用是将数据和相关的操作封装在一起,以创建可重用和模块化的代码。

  • 类(Class):一个类是一种数据结构,用于定义对象的属性和行为。
    • 类包含属性(变量)、方法(通用方法)
  • 对象(Object):一个对象是类的实例化(即根据类创建的具体对象)。
    • 调用类的方法

        

类与对象本身比较抽象,我们将其带入到现实中作比喻:

我们每个人类需要做很多事,这些事情存在不同的类型,也有大事小事之分,所以需要通过不同的类来区分不同的事情。

【类1】我名下有一辆汽车,那么需要对汽车封装一个类:

  • 类的属性:汽车的颜色、品牌、型号等。
  • 类的方法:汽车的功能 加速、刹车、转弯等。
  • 对象:将汽车实例化,通过这个对象去调用,而不是每次繁琐的去执行那些方法。

【类2】我有一部手机,那么再对这个手机封装一个类:

  • 类的属性:手机的品牌、型号、尺寸等。
  • 类的方法:手机的功能 拍照、打电话、发送短信等。
  • 对象:将手机实例化,也是通过对象去调用这个类

类的主要作用就是将大事分成不同的类,大事中的细节通过方法去定义。

        

大致流程如下:

        

类与函数的区别

    "函数" 的使用方法与 "类" 实际上是差不多的,一般在封装一个独立的方法时,直接使用函数就行。如果是编写一个小程序,那么这个小程序会存在大量关联性的方法,这时候函数就显得有点无助。比如封装一个购物车,购物车肯定包含 加入购物车、删除商品、结算商品,计算价格等方法,这些方法都具有一定的关联性。像一部分变量是公用的,使用函数的话只能将这些变量全局化,但全局化又会面临另一个问题,整个程序的变量非常多,肯定存在大量相同的变量(这种情况难以处理)。如果使用类来处理,类中的变量对这个类里面的方法是公用的,对其他类是隔离的,所以能够避免变量名相同的劣势。

    类也支持继承的概念,可以通过继承从其他类获取属性和方法,并进行扩展和定制。并且可以共享类的属性,即类的所有实例对象可以访问和修改类的属性。所以大功能使用类,独立功能使用函数。

        

2. 基础用法

2.1. 定义类

python 使用关键字 class 来定义一个类,比如:

class 类名(继承对象):
    代码块
  • 类名:自定义的名称,通常以大写字母开头,按照惯例采用驼峰命名法。例如:MyClass。
  • 继承对象:python允许继承另一个类,比如超类(object)或者其他类名、库等。
  • 代码块:自定义的一段代码,只作用于这个类,必须使用缩进来定义。

        

定义一个最简单的类:

# 定义一个类,继承超类(提供一些通用的方法和属性)
class MyClass(object):    # 在python3中默认继承object,可以为空
    # 在类中编写代码
    print("这是一个类!")

# 创建一个类的实例
mc = MyClass()

        

2.2. 定义属性

属性实际上就是在类中定义的变量,作用于类中。属性包含:类属性、私有类属性、实例属性、方法属性。

  • 类属性:整个类共享,静态方法无法共享。
  • 私有类属性:整个类共享,静态方法无法共享,外部无法调用。
  • 实例属性:所有实例共享,类方法、静态方法无法共享。
  • 方法属性:定义在某个方法中的属性,仅该方法使用。

当这些属性名相同时,在方法中调用属性的优先级为:

方法属性 > 实例属性 > 类属性

        

举一个简单的例子

# 定义一个类
class MyClass(object):
    x = 10    # 定义的属性

    def func(self):    # 定义的方法
        print(f"这是类中的一个方法, 属性x为:{self.x}")  # 共享类属性

# 创建一个类的实例
mc = MyClass()
mc.func()    # 调用类中的方法

        

注意self 是一个特殊的参数,用于引用当前对象(实例)。能够使得类的方法可以与对象实例进行交互,访问和修改对象的属性,并调用其他方法。若不使用self参数,在方法中无法直接访问和操作对象实例的属性和方法。如下:

class MyClass(object):
    def __init__(x):
        x = x

    def func():
        print(x)

mc = MyClass(10)    # 实例化对象,传入一个参数
mc.func()  # 调用其中一个方法

无法实例化,并且 func 方法也无法调用属性x 

        

常见3种定义属性的方法

【方案一】直接定义属性(类属性)

# 定义一个类
class MyClass(object):
    x = 10    # 定义一个共享属性

    def func1(self):    # 定义方法1
        print(f"方法1中引用属性x:{self.x}")

    def func2(self):    # 定义方法2
        print(f"方法2中引用属性x:{self.x}")

mc = MyClass()    # 创建一个类的实例
mc.func1()    # 调用方法1
mc.func2()    # 调用方法2

在类中直接定义的属性具有类级别的作用域,可以被类的所有实例对象所共享。但如果需要为不同的实例对象定义不同的属性值,就需要在每个实例对象的构造方法中进行定义,或者使用其他方式来实现。

        

【方案二】通过构造方法 __init__() 来定义

类中直接定义的属性是在类的层级下直接定义的,而__init__方法是类的构造方法,在实例化对象时被调用来初始化对象的属性。

class MyClass(object):
    def __init__(self, x):  # 初始化一个实例属性
        self.x = x  # 使用self.x引用变量x

mc = MyClass(10)    # 实例化时需要传入一个参数(自定义)
  • x:是方法中的一个局部变量,只在 __init__ 中有效,用于接收传入的参数值。
  • self.x:是实例属性,与x是相互独立的,用来引用x变量,在后续作用到整个类。

比如:使用两个属性都接收变量 x

class MyClass(object):
    def __init__(self, x):  # 初始化一个实例属性
        self.x = x  # 使用self.x引用变量x
        self.y = x  # 使用self.y引用变量x

    def func(self):
        print(f"变量self.x的值为:{self.x}")
        print(f"变量self.y的值为:{self.y}")

mc = MyClass(10)    # 实例化对象,传入一个参数
mc.func()    # 调用方法

这个变量同样支持默认值,写法与函数一样

def __init__(self, x=默认值):

如果需要多个参数则使用逗号分开

def __init__(self, x=默认值, y, z):

        

【方案三】通过自定义方法动态定义属性

上面两种属性都可以作用到整个类全局,第3种方案就是在方法中定义属性

class MyClass(object):
    def func(self, x):    # 封装一个方法
        self.x = x
        print(self.x)

mc = MyClass()    # 实例化对象
mc.func(10)  # 调用方法需要传入一个参数

在方法中定义属性与第2种方案是类似了,同样可以定义多个参数和默认值

def func(self, x=默认值, y, z):

        

2.3. 定义方法

方法实际上就是在类中定义的函数,作用于类中。

class MyClass(object):    # 定义一个类
    def func(self, x):    # 封装一个方法
        pass

方法间通过关键字 self 来相互调用

class MyClass(object):    # 定义一个类
    def func1(self):      # 封装第一个方法
        print("我是方法1")

    def func2(self):    # 封装第二个方法
        print("我是方法2")
        self.func1()    # 调用方法1

MyClass().func2()    # 调用方法2

        

上面的方法都是共有方法,而在 python 中还有一个私有方法(需要以双下划线开头)

class MyClass(object):
    def __func1(self):  # 私有方法(以双下划线开头)
        print('这是一个私有方法')

    def func2(self):    # 公共方法
        print('这是一个普通方法')
        self.__func1()  # 内部调用这个私有方法

代码中定义了2个方法,一个私有,一个共有。私有方法在外部无法调用

mc = MyClass()   # 实例化这个对象
mc.__func1()     # 调用这个私有方法

这种方法只能在内部调用,代码 func2 中调用了 __func1,所以直接在外部调用 func2

mc = MyClass()   # 实例化这个对象
mc.func2()       # 调用这个公共方法

        

3. 类的继承

继承是通过创建一个子类来继承另一个已经定义的类的属性和方法。流程图如下:

  • 爷爷类没有继承任何其他类,所以他只享有自己的属性和方法;
  • 爸爸类和叔叔类都继承了爷爷类,所以他们可以共享爷爷类的属性和方法,但爸爸和叔叔的属性、方法互不干涉;
  • 儿子类继承了爸爸类,他可以共享爸爸类的属性和方法;由于爸爸继承了爷爷,所以儿子也共享爷爷的属性和方法;
  • 侄儿类同时继承叔叔和爸爸,所以他可以同时共享叔叔类和爸爸类的属性和方法;由于叔叔和爸爸也继承了爷爷,所以侄儿同样继承爷爷的属性和方法;
  • 孙子类继承了儿子和侄儿,所以孙子可以共享儿子类和侄儿类的属性和方法;由于儿子和侄儿也继承了爸爸、叔叔、爷爷,所以孙子可以共享所有类的属性和方法。

每个类可以使用的属性和方法如下:

爷爷:本身
爸爸:本身、爷爷
叔叔:本身、爷爷
儿子:本身、爸爸、爷爷
侄儿:本身、爸爸、叔叔、爷爷
孙子:本身、儿子、侄儿、爸爸、叔叔、爷爷

        

3.1. 单继承

定义 A、B两个类,子类可以继承父类中的所有属性和方法

class A(object):    # 定义一个类
    x = 10
    def func_a(self):
        print("我是A类中的方法")

class B(A):    # 继承A类(包括全部属性和方法)
    def func_b(self):
        print(f"我是B类中的方法,属性x为:{self.x}")

B().func_b()    # 调用B类中的方法
B().func_a()    # 调用B类中继承的方法

        

子类重写】如果子类与父类的方法名或属性名相同,调用子类时默认使用子类的方法或属性

class A(object):    # 定义A类
    a = 1
    def func1(self):
        print('A类')

class B(A):    # 继承A类
    a = 2      # 属性与A类相同
    def func1(self):    # 方法也与A类相同
        print(f"B类,属性a为:{self.a}")  # 使用当前类的属性

B().func1()    # 调用子类

        

3.2. 多继承

当继承多个类时,这些类的属性名或方法名相同时,默认使用继承的第一个父类

class A(object):
    a = 1    # 父类定义属性a

class B(object):
    a = 2    # 父类定义属性a

class C(A, B):  # 先继承A,后继承B
    def func1(self):
        print(f"属性a的值为:{self.a}")  # 默认调用A的属性

C().func1() # 调用子类C

        

3.3. 连续继承

当出现连续继承时,最小的子类可以使用上面全部父类的属性和方法

class A(object):    # 定义第1个类
    def func1(self):
        print('A类')

class B(A):     # 定义第2个类,继承第1个
    def func2(self):
        print('B类')

class C(B):     # 定义第3个类,继承第2个
    pass

C().func1()     # 使用第3个类的实例调用第1个类的方法
C().func2()     # 使用第3个类的实例调用第2个类的方法

        

当连续继承太多,相同的方法名也比较多,我们也可以使用 super 函数去调用上一个父类的方法

class A(object):    # 定义第1个类
    def func(self): # 方法名与第2个类相同
        print('A类')

class B(A):         # 定义第2个类
    def func(self): # 方法名与第1个类相同
        print('B类')

class C(B):         # 定义第3个类
    def func(self): # 方法名与第1个和第2个类相同
        super().func()  # 调用上一级类方法,不需要指定类名

C().func()      # 调用子类的方法

注意super 只能调用上一级,调用其他可以直接指定类名

A.func(self)
B.func(self)

        

它们的继承的关系可以通过 __mro__ 查看

class A(object):    # 继承超类
    pass

class B(A):     # 继承第1个类
    pass

class C(B):     # 继承第2个类
    pass

print(C.__mro__)  # 查看C类的继承关系

        

3.4. 多态性

多态性是通过继承和方法重写来实现的,允许不同的对象对相同的方法作出不同的响应,从而增加了代码的灵活性和可扩展性。

封装4个类,1个作为父类,另外三个作为子类,它们的方法名称是一样的

class Animal:    # 父类
    '''动物类'''
    name = '[3号]'

class Dog(Animal):    # 继承动物类
    '''小狗类'''
    def sound(self):
        print(f"{self.name} 汪汪汪!")

class Cat(Animal):    # 继承动物类
    '''小猫类'''
    def sound(self):
        print(f"{self.name} 喵喵喵!")

class Cow(Animal):    # 继承动物类
    '''小牛类'''
    def sound(self):
        print(f"{self.name} 哞哞哞!")

4个类中的方法名完全一致,这时我们来封装一个函数,用来调用这些类(因为方法名是一样的,调用一个即可)

def make_sound(x):    # 需要传入一个x参数
'''x参数就是类名,通过传入的类名去调用方法'''
    x.sound()    # sound方法是4个类中的方法

最后我们来实例化这几个动物类

dog = Dog()    # 实例化小狗类
cat = Cat()    # 实例化小猫类
cow = Cow()    # 实例化小牛类

        

向函数中传入小狗类

make_sound(dog)    # 调用函数,传参为小狗类

        

向函数中传入小猫类

make_sound(cat)    # 调用函数,传参为小猫类

        

向函数中传入小牛类

make_sound(cow)    # 调用函数,传参为小牛类

        

4. 类的装饰器

装饰器是一个用于修改函数或类的行为的特殊函数或类,它可以在不修改原始代码的情况下,添加额外的功能或行为,提供了一种简洁的方式来修改或增强函数或类的功能。

4.1. 类方法 @classmethod

类方法是作用于整个类而不是实例的方法。它们可以访问和修改类的属性,提供额外的构造方法,实现多态行为,封装和管理类相关的功能,与类级别数据进行交互。类方法使用 @classmethod 装饰器进行标识,第一个参数通常被命名为 cls,表示类本身。

我们在类中定义一个实例方法和一个类方法,看一下两者之间的调用区别:

class MyClass(object):
    def func1(self):    # 封装一个实例方法
        print('我是实例方法')

    @classmethod        # 特殊标识符
    def func2(cls):     # 封装一个类方法
        print('我是类方法')

在调用时,实例方法需要初始化实例,而类方法不需要

m = MyClass()
m.func1()    # 调用实例方法,需要实例化

MyClass.func2()  # 调用类方法,不需要实例化

        

为什么调用类方法不需要实例化?

因为声明的类方法就是类的本身,形参 cls 就相当于类名,所以本身调用本身不需要实例化。实例化是用来调用实例方法的

        

类方法使用类属性时,以 cls 方式调用

class MyClass(object):
    x = 1    # 定义一个属性

    @classmethod
    def func(cls):  # 使用cls
        print(f'公共类属性a为{cls.x}')

MyClass.func()    # 调用类方法

        

注意:类方法无法调用实例属性,仅适用于类级别上的操作。

class MyClass(object):
    def __init__(self):
        self.x = 1    # 定义实例属性

    @classmethod
    def func(cls):
        print(f'实例属性a为{cls.x}')  # 无法调用

MyClass.func()    # 调用类方法

        

4.2. 静态方法 @staticmethod

静态方法既不需要传递类对象,也不需要传递实例对象(没有形参)。静态方法可以独立于类的任何实例或类级别而存在,因此它们是最为模块化、灵活和可维护的方法类型之一。

特点

  1. 静态方法不能访问类的变量。
  2. 静态方法不需要特殊的参数,但仍然可以使用默认参数。
  3. 静态方法往往更容易维护和测试,因为它们不会改变且不依赖于类的状态。
  4. 由于静态方法可以独立于类而存在,它们也可以很方便地跨越模块、包和应用程序。

静态方法的调用与类方法类似,不需要实例化对象

class MyClass():
    @staticmethod   # 特殊标识符
    def func(n):    # 定义一个静态方法
        print(f"我是一个静态方法,属性n为:{n}")  # 调用属性不需要关键字

MyClass.func(10)  # 调用静态方法,传入一个参数

        

由于静态方法本身是独立的,所以它无法调用实例属性和实例方法。当然,实例方法也无法调用静态方法。

class MyClass(object):
    def func1(self):    # 定义一个实例方法
        print('实例方法')

    @staticmethod
    def func2():        # 定义一个静态方法
        func1()  # 直接调用实例方法(无法调用)
        MyClass().func1()  # 通过类调用实例方法(无法调用)

MyClass.func()    # 调用静态方法

        

总结

  • 静态方法不需要实例化对象
  • 静态方法是独立存在的,无法调用其他实例方法、实例属性,也无法被继承。

        

4.3. 类方法与静态方法的区别

1、从参数的角度来看

  • 类方法第一个参数默认 cls 代表类的本身,非实例本身。
  • 静态方法不需要额外的形参,可以把它当成一个独立函数。

        

2、从属性角度来看

  • 类方法可以访问类中的其他属性。
  • 静态方法不可以访问类中的其他属性。

        

3、从继承角度来看

  • 类方法可以被继承和重写。
  • 静态方法无法被继承。

        

4、从用途角度来看

  • 类方法通常用于操作与类有关的属性和方法,适用于通用代码。
  • 静态方法通常用于实现可维护性高的代码,适用于独立和复用的代码块。

        

5. 应用场景

5.1. 汽车案例

定义一个汽车的类,具有品牌、型号和颜色等属性,封装汽车详细信息的方法、汽车数量的方法、检查颜色合法性的方法。

# 封装一个表示汽车的类
class Car(object):
    car_count = 0   # 调用类的计数器

    def __init__(self, brand, model, color):
        self.brand = brand  # 汽车品牌
        self.model = model  # 汽车型号
        self.color = color  # 汽车颜色

        Car.car_count += 1  # 每次创建一个Car对象,计数增加1

    def get_car_info(self):
        '''封装一个获取汽车的详细信息的实例方法'''
        return f"品牌: {self.brand}, 型号: {self.model}, 颜色: {self.color}"

    @classmethod
    def get_total_count(cls):
        '''封装一个获取总的汽车数量类方法'''
        return cls.car_count

    @staticmethod
    def is_valid_color(color):
        '''封装一个用于检查是否是合法的颜色的静态方法'''
        valid_colors = ["red", "blue", "green"]
        return color.lower() in valid_colors

我们来调用这个类

# 创建两个Car对象,并传入品牌、型号和颜色
car1 = Car("Tesla", "Model 3", "red")
car2 = Car("Toyota", "Camry", "blue")

# 调用实例方法,获取汽车的详细信息
print(car1.get_car_info())
print(car2.get_car_info())

# 调用类方法,获取总的汽车数量
print("汽车数量为: {}台".format(Car.get_total_count()))

# 调用静态方法,检查颜色是否合法
print(Car.is_valid_color("red"))
print(Car.is_valid_color("yellow"))

        

5.2. 电视案例

定义一个电视的类,封装一些打开电视、关闭电视、切换频道的方法

# 定义一个电视类
class TV:
    def __init__(self, brand, size):
        '''初始化方法,用于创建TV对象时的初始设置'''
        self.brand = brand  # 品牌
        self.size = size    # 尺寸
        self.is_on = False  # 电视开关(默认关闭)
        self.volume = 50    # 音量(默认50)
        self.channel = 1    # 电视频道(默认1)

    def turn_on(self):
        '''封装一个实例方法,打开电视'''
        self.is_on = True

    def turn_off(self):
        '''封装一个实例方法,关闭电视'''
        self.is_on = False

    def change_channel(self, channel):
        '''封装一个实例方法,切换电视频道'''
        self.channel = channel

    def volume_up(self):
        '''封装一个实例方法,增加音量'''
        if self.volume < 100:
            self.volume += 1

    def volume_down(self):
        '''封装一个实例方法,减小音量'''
        if self.volume > 0:
            self.volume -= 1

我们来调用这个类

# 创建一个TV对象
tv = TV("Sony", 55)

# 打开电视
tv.turn_on()

# 切换到第5个频道
tv.change_channel(5)

# 增加音量
tv.volume_up()

# 打印电视信息
print(f"品牌: {tv.brand}, 尺寸: {tv.size}, 频道: {tv.channel}, 音量: {tv.volume}")

# 关闭电视
tv.turn_off()

  • 28
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值